headlamp 0.1.10 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -93,7 +93,7 @@ var init_TimeoutError = __esm({
93
93
 
94
94
  // node_modules/es-toolkit/dist/promise/delay.mjs
95
95
  function delay(ms, { signal } = {}) {
96
- return new Promise((resolve9, reject) => {
96
+ return new Promise((resolve10, reject) => {
97
97
  const abortError = () => {
98
98
  reject(new AbortError());
99
99
  };
@@ -106,7 +106,7 @@ function delay(ms, { signal } = {}) {
106
106
  }
107
107
  const timeoutId = setTimeout(() => {
108
108
  signal?.removeEventListener("abort", abortHandler);
109
- resolve9();
109
+ resolve10();
110
110
  }, ms);
111
111
  signal?.addEventListener("abort", abortHandler, { once: true });
112
112
  });
@@ -171,11 +171,11 @@ var init_exec = __esm({
171
171
  child.stderr?.on("data", (chunk) => {
172
172
  stderr += String(chunk);
173
173
  });
174
- const exec = new Promise((resolve9, reject) => {
174
+ const exec = new Promise((resolve10, reject) => {
175
175
  child.on("error", reject);
176
176
  child.on(
177
177
  "close",
178
- (code) => Number(code) === 0 ? resolve9(stdout) : reject(new Error(stderr || `exit ${code}`))
178
+ (code) => Number(code) === 0 ? resolve10(stdout) : reject(new Error(stderr || `exit ${code}`))
179
179
  );
180
180
  });
181
181
  try {
@@ -195,7 +195,7 @@ var init_exec = __esm({
195
195
  throw caughtError;
196
196
  }
197
197
  };
198
- runExitCode = async (cmd, args, opts = {}) => new Promise((resolve9, reject) => {
198
+ runExitCode = async (cmd, args, opts = {}) => new Promise((resolve10, reject) => {
199
199
  const child = (0, import_node_child_process.spawn)(cmd, [...args], {
200
200
  cwd: opts.cwd,
201
201
  env: opts.env,
@@ -204,9 +204,9 @@ var init_exec = __esm({
204
204
  windowsHide: true
205
205
  });
206
206
  child.on("error", reject);
207
- child.on("close", (code) => resolve9(Number(code)));
207
+ child.on("close", (code) => resolve10(Number(code)));
208
208
  });
209
- runWithCapture = async (cmd, args, opts) => new Promise((resolve9, reject) => {
209
+ runWithCapture = async (cmd, args, opts) => new Promise((resolve10, reject) => {
210
210
  const child = (0, import_node_child_process.spawn)(cmd, [...args], {
211
211
  cwd: opts.cwd,
212
212
  env: opts.env,
@@ -222,7 +222,7 @@ var init_exec = __esm({
222
222
  buf += String(chunk);
223
223
  });
224
224
  child.on("error", reject);
225
- child.on("close", (code) => resolve9({ code: Number(code), output: buf }));
225
+ child.on("close", (code) => resolve10({ code: Number(code), output: buf }));
226
226
  });
227
227
  }
228
228
  });
@@ -272,7 +272,7 @@ var init_args = __esm({
272
272
  isSome = (opt) => opt._tag === "some";
273
273
  step = (actions, skipNext = false) => [actions, skipNext];
274
274
  rule = {
275
- when: (predicate, build) => (value, env2) => predicate(value, env2) ? Some(build(value, env2)) : None,
275
+ when: (predicate, build) => (value, env) => predicate(value, env) ? Some(build(value, env)) : None,
276
276
  eq: (flag, build) => rule.when(
277
277
  (value) => value === flag,
278
278
  () => build()
@@ -282,12 +282,12 @@ var init_args = __esm({
282
282
  (value) => build(value)
283
283
  ),
284
284
  inSet: (select, build) => rule.when(
285
- (value, env2) => select(env2).has(value),
285
+ (value, env) => select(env).has(value),
286
286
  (value) => build(value)
287
287
  ),
288
288
  withLookahead: (lookaheadFlag, build) => rule.when(
289
- (value, env2) => value === lookaheadFlag && typeof env2.lookahead === "string" && env2.lookahead.length > 0,
290
- (value, env2) => build(value, env2.lookahead)
289
+ (value, env) => value === lookaheadFlag && typeof env.lookahead === "string" && env.lookahead.length > 0,
290
+ (value, env) => build(value, env.lookahead)
291
291
  )
292
292
  };
293
293
  STRING_EMPTY = "";
@@ -420,7 +420,7 @@ var init_args = __esm({
420
420
  ),
421
421
  rule.startsWith("--testPathPattern=", (value) => step([ActionBuilders.jestArg(value)])),
422
422
  rule.inSet(
423
- (env2) => env2.jestFlags,
423
+ (env) => env.jestFlags,
424
424
  (value) => step([ActionBuilders.jestArg(value)])
425
425
  ),
426
426
  rule.when(
@@ -516,9 +516,9 @@ var init_args = __esm({
516
516
  }
517
517
  const tokenValue = token ?? STRING_EMPTY;
518
518
  const nextToken = tokens[index + INDEX_STEP];
519
- let env2 = { jestFlags: jestOnlyFlags };
519
+ let env = { jestFlags: jestOnlyFlags };
520
520
  if (typeof nextToken === "string" && nextToken.length > 0) {
521
- env2 = { jestFlags: jestOnlyFlags, lookahead: nextToken };
521
+ env = { jestFlags: jestOnlyFlags, lookahead: nextToken };
522
522
  }
523
523
  const firstMatch = (rs, value, envForRules) => {
524
524
  for (const ruleFn of rs) {
@@ -529,7 +529,7 @@ var init_args = __esm({
529
529
  }
530
530
  return None;
531
531
  };
532
- const matched = firstMatch(rules, tokenValue, env2);
532
+ const matched = firstMatch(rules, tokenValue, env);
533
533
  const isTestFileToken = (candidate) => /\.(test|spec)\.[tj]sx?$/.test(candidate) || /(^|\/)tests?\//.test(candidate);
534
534
  const isPathLike = (candidate) => /[\\/]/.test(candidate) || /\.(m?[tj]sx?)$/i.test(candidate);
535
535
  const [matchedActions, shouldSkipNext] = isSome(matched) ? matched.value : (() => {
@@ -736,12 +736,14 @@ __export(fast_related_exports, {
736
736
  cachedRelated: () => cachedRelated,
737
737
  findRelatedTestsFast: () => findRelatedTestsFast
738
738
  });
739
- var path2, fs, TailSegmentCount, EmptyCount, JsonIndentSpaces, DEFAULT_TEST_GLOBS, findRelatedTestsFast, cachedRelated;
739
+ var path2, os2, fs, import_node_crypto, TailSegmentCount, EmptyCount, JsonIndentSpaces, DEFAULT_TEST_GLOBS, findRelatedTestsFast, cachedRelated;
740
740
  var init_fast_related = __esm({
741
741
  "src/lib/fast-related.ts"() {
742
742
  "use strict";
743
743
  path2 = __toESM(require("node:path"), 1);
744
+ os2 = __toESM(require("node:os"), 1);
744
745
  fs = __toESM(require("node:fs/promises"), 1);
746
+ import_node_crypto = require("node:crypto");
745
747
  init_env_utils();
746
748
  init_exec();
747
749
  TailSegmentCount = 2;
@@ -790,9 +792,7 @@ var init_fast_related = __esm({
790
792
  }
791
793
  const args = ["-n", "-l", "-S", "-F"];
792
794
  testGlobs.forEach((globPattern) => args.push("-g", globPattern));
793
- excludeGlobs.forEach(
794
- (excludeGlobPattern) => args.push("-g", `!${excludeGlobPattern}`)
795
- );
795
+ excludeGlobs.forEach((excludeGlobPattern) => args.push("-g", `!${excludeGlobPattern}`));
796
796
  seeds.forEach((seedToken) => args.push("-e", seedToken));
797
797
  let raw = "";
798
798
  try {
@@ -804,9 +804,7 @@ var init_fast_related = __esm({
804
804
  }
805
805
  const lines = raw.split(/\r?\n/).map((lineText) => lineText.trim()).filter(Boolean);
806
806
  const looksLikeTest = (pathText) => /\.(test|spec)\.[tj]sx?$/i.test(pathText) || /(^|\/)tests?\//i.test(pathText);
807
- const absolute = lines.map(
808
- (relativePath) => path2.resolve(repoRoot, relativePath).replace(/\\/g, "/")
809
- ).filter(looksLikeTest);
807
+ const absolute = lines.map((relativePath) => path2.resolve(repoRoot, relativePath).replace(/\\/g, "/")).filter(looksLikeTest);
810
808
  const uniq = Array.from(new Set(absolute));
811
809
  const results = [];
812
810
  await Promise.all(
@@ -821,17 +819,15 @@ var init_fast_related = __esm({
821
819
  return results;
822
820
  };
823
821
  cachedRelated = async (opts) => {
824
- const cacheDir = path2.join(opts.repoRoot, ".cache");
822
+ const cacheRoot = process.env.HEADLAMP_CACHE_DIR || path2.join(os2.tmpdir(), "headlamp-cache");
823
+ const repoKey = (0, import_node_crypto.createHash)("sha1").update(path2.resolve(opts.repoRoot)).digest("hex").slice(0, 12);
824
+ const cacheDir = path2.join(cacheRoot, repoKey);
825
825
  const cacheFile = path2.join(cacheDir, "relevant-tests.json");
826
826
  let head = "nogit";
827
827
  try {
828
- const raw = await runText(
829
- "git",
830
- ["-C", opts.repoRoot, "rev-parse", "--short", "HEAD"],
831
- {
832
- env: safeEnv(process.env, {})
833
- }
834
- );
828
+ const raw = await runText("git", ["-C", opts.repoRoot, "rev-parse", "--short", "HEAD"], {
829
+ env: safeEnv(process.env, {})
830
+ });
835
831
  head = raw.trim() || "nogit";
836
832
  } catch {
837
833
  head = "nogit";
@@ -863,10 +859,7 @@ var init_fast_related = __esm({
863
859
  try {
864
860
  const next = { ...bag, [key]: Array.from(new Set(recomputed)) };
865
861
  await fs.mkdir(cacheDir, { recursive: true });
866
- await fs.writeFile(
867
- cacheFile,
868
- JSON.stringify(next, null, JsonIndentSpaces)
869
- );
862
+ await fs.writeFile(cacheFile, JSON.stringify(next, null, JsonIndentSpaces));
870
863
  } catch {
871
864
  }
872
865
  return recomputed;
@@ -1987,19 +1980,19 @@ var require_lib = __commonJS({
1987
1980
  "node_modules/json5/lib/index.js"(exports2, module2) {
1988
1981
  var parse = require_parse();
1989
1982
  var stringify = require_stringify();
1990
- var JSON52 = {
1983
+ var JSON53 = {
1991
1984
  parse,
1992
1985
  stringify
1993
1986
  };
1994
- module2.exports = JSON52;
1987
+ module2.exports = JSON53;
1995
1988
  }
1996
1989
  });
1997
1990
 
1998
1991
  // src/lib/program.ts
1999
- var path10 = __toESM(require("node:path"), 1);
2000
- var os2 = __toESM(require("node:os"), 1);
1992
+ var path11 = __toESM(require("node:path"), 1);
1993
+ var os3 = __toESM(require("node:os"), 1);
2001
1994
  var fsSync3 = __toESM(require("node:fs"), 1);
2002
- var fs5 = __toESM(require("node:fs/promises"), 1);
1995
+ var fs7 = __toESM(require("node:fs/promises"), 1);
2003
1996
  var LibReport = __toESM(require("istanbul-lib-report"), 1);
2004
1997
  var Reports = __toESM(require("istanbul-reports"), 1);
2005
1998
  var import_istanbul_lib_coverage2 = require("istanbul-lib-coverage");
@@ -3370,7 +3363,7 @@ var printCompactCoverage = async (opts) => {
3370
3363
  );
3371
3364
  }
3372
3365
  };
3373
- var shortenPathPreservingFilename = (relPath, maxWidth, opts) => {
3366
+ var shortenPathPreservingFilename = (relPath2, maxWidth, opts) => {
3374
3367
  const ellipsis = opts?.ellipsis ?? "\u2026";
3375
3368
  const START_HEAD = Math.max(0, opts?.keepHead ?? 1);
3376
3369
  const START_TAIL = Math.max(0, opts?.keepTail ?? 1);
@@ -3542,7 +3535,7 @@ var shortenPathPreservingFilename = (relPath, maxWidth, opts) => {
3542
3535
  }
3543
3536
  return null;
3544
3537
  };
3545
- const normalized = relPath.replace(/\\/g, "/");
3538
+ const normalized = relPath2.replace(/\\/g, "/");
3546
3539
  if (visibleWidth(normalized) <= maxWidth) {
3547
3540
  return normalized;
3548
3541
  }
@@ -3998,11 +3991,343 @@ var printPerFileCompositeTables = async (opts) => {
3998
3991
  }
3999
3992
  };
4000
3993
 
4001
- // src/lib/jest-bridge.ts
4002
- var path9 = __toESM(require("node:path"), 1);
4003
- var fs4 = __toESM(require("node:fs"), 1);
4004
- var util = __toESM(require("node:util"), 1);
4005
- var import_json5 = __toESM(require_lib(), 1);
3994
+ // src/lib/jest-reporter-source.ts
3995
+ var JEST_BRIDGE_REPORTER_SOURCE = `const fs = require('fs');
3996
+ const path = require('path');
3997
+
3998
+ const isObject = (v) => typeof v === 'object' && v !== null;
3999
+ const sanitizeError = (err) => {
4000
+ if (!isObject(err)) return err;
4001
+ const out = {};
4002
+ const name = err.name || (err.constructor && err.constructor.name) || undefined;
4003
+ if (name) out.name = String(name);
4004
+ if (typeof err.message === 'string') out.message = err.message;
4005
+ if (typeof err.stack === 'string') out.stack = err.stack;
4006
+ if (err.code !== undefined) out.code = err.code;
4007
+ if (err.expected !== undefined) out.expected = err.expected;
4008
+ if (err.received !== undefined) out.received = err.received;
4009
+ if (err.matcherResult && isObject(err.matcherResult)) {
4010
+ const mr = err.matcherResult;
4011
+ let messageText;
4012
+ try {
4013
+ messageText = typeof mr.message === 'function' ? String(mr.message()) : (typeof mr.message === 'string' ? mr.message : undefined);
4014
+ } catch {}
4015
+ out.matcherResult = {
4016
+ matcherName: typeof mr.matcherName === 'string' ? mr.matcherName : undefined,
4017
+ message: messageText,
4018
+ stack: typeof mr.stack === 'string' ? mr.stack : undefined,
4019
+ expected: mr.expected,
4020
+ received: mr.received,
4021
+ actual: mr.actual,
4022
+ pass: typeof mr.pass === 'boolean' ? mr.pass : undefined,
4023
+ };
4024
+ }
4025
+ if (err.cause) {
4026
+ out.cause = sanitizeError(err.cause);
4027
+ }
4028
+ // Copy own enumerable props to preserve custom data
4029
+ try {
4030
+ for (const key of Object.keys(err)) {
4031
+ if (!(key in out)) out[key] = err[key];
4032
+ }
4033
+ } catch {}
4034
+ return out;
4035
+ };
4036
+ const sanitizeDetail = (d) => {
4037
+ if (typeof d === 'string') return d;
4038
+ if (!isObject(d)) return d;
4039
+ // Common Jest detail shapes
4040
+ const out = {};
4041
+ if (d.message) out.message = d.message;
4042
+ if (d.stack) out.stack = d.stack;
4043
+ if (d.error) out.error = sanitizeError(d.error);
4044
+ if (d.matcherResult) out.matcherResult = sanitizeError({ matcherResult: d.matcherResult }).matcherResult;
4045
+ if (d.expected !== undefined) out.expected = d.expected;
4046
+ if (d.received !== undefined) out.received = d.received;
4047
+ // Copy the rest
4048
+ try {
4049
+ for (const key of Object.keys(d)) {
4050
+ if (!(key in out)) out[key] = d[key];
4051
+ }
4052
+ } catch {}
4053
+ return out;
4054
+ };
4055
+
4056
+ class BridgeReporter {
4057
+ constructor(globalConfig, options) {
4058
+ this.out = process.env.JEST_BRIDGE_OUT || (options && options.outFile) || path.join(process.cwd(), 'coverage', 'jest-run.json');
4059
+ this.buf = { startTime: Date.now(), testResults: [], aggregated: null };
4060
+ }
4061
+ onRunStart() { this.buf.startTime = Date.now(); }
4062
+ onTestResult(_test, tr) {
4063
+ const mapAssertion = (a) => ({
4064
+ title: a.title,
4065
+ fullName: a.fullName || [...(a.ancestorTitles || []), a.title].join(' '),
4066
+ status: a.status,
4067
+ duration: a.duration || 0,
4068
+ location: a.location || null,
4069
+ failureMessages: (a.failureMessages || []).map(String),
4070
+ failureDetails: (a.failureDetails || []).map(sanitizeDetail),
4071
+ });
4072
+ this.buf.testResults.push({
4073
+ testFilePath: tr.testFilePath,
4074
+ status: tr.numFailingTests ? 'failed' : 'passed',
4075
+ failureMessage: tr.failureMessage || '',
4076
+ failureDetails: (tr.failureDetails || []).map(sanitizeDetail),
4077
+ testExecError: tr.testExecError ? sanitizeError(tr.testExecError) : null,
4078
+ console: tr.console || null,
4079
+ perfStats: tr.perfStats || {},
4080
+ testResults: (tr.testResults || []).map(mapAssertion),
4081
+ });
4082
+ }
4083
+ onRunComplete(_contexts, agg) {
4084
+ this.buf.aggregated = {
4085
+ numTotalTestSuites: agg.numTotalTestSuites,
4086
+ numPassedTestSuites: agg.numPassedTestSuites,
4087
+ numFailedTestSuites: agg.numFailedTestSuites,
4088
+ numTotalTests: agg.numTotalTests,
4089
+ numPassedTests: agg.numPassedTests,
4090
+ numFailedTests: agg.numFailedTests,
4091
+ numPendingTests: agg.numPendingTests,
4092
+ numTodoTests: agg.numTodoTests,
4093
+ startTime: agg.startTime,
4094
+ success: agg.success,
4095
+ runTimeMs: agg.testResults.reduce((t, r) => t + Math.max(0, (r.perfStats?.end || 0) - (r.perfStats?.start || 0)), 0),
4096
+ };
4097
+ fs.mkdirSync(path.dirname(this.out), { recursive: true });
4098
+ fs.writeFileSync(this.out, JSON.stringify(this.buf), 'utf8');
4099
+ }
4100
+ }
4101
+ module.exports = BridgeReporter;`;
4102
+
4103
+ // src/lib/jest-environment-source.ts
4104
+ var JEST_BRIDGE_ENV_SOURCE = `
4105
+ 'use strict';
4106
+
4107
+ const NodeEnvironment = require('jest-environment-node').TestEnvironment || require('jest-environment-node');
4108
+
4109
+ module.exports = class BridgeEnv extends NodeEnvironment {
4110
+ constructor(config, context) {
4111
+ super(config, context);
4112
+ const { AsyncLocalStorage } = require('node:async_hooks');
4113
+ this._als = new AsyncLocalStorage();
4114
+ this._cleanup = [];
4115
+ }
4116
+
4117
+ _ctx() {
4118
+ try { const s = this._als.getStore(); if (s) return s; } catch {}
4119
+ try {
4120
+ const st = this.global.expect && typeof this.global.expect.getState === 'function' ? this.global.expect.getState() : {};
4121
+ return { testPath: st.testPath, currentTestName: st.currentTestName };
4122
+ } catch { return {}; }
4123
+ }
4124
+
4125
+ async setup() {
4126
+ await super.setup();
4127
+
4128
+ try { Error.stackTraceLimit = Math.max(Error.stackTraceLimit || 10, 50); } catch {}
4129
+
4130
+ const print = (payload) => { try { this.global.console.error('[JEST-BRIDGE-EVENT]', JSON.stringify(payload)); } catch {} };
4131
+ const toErr = (x) => { try { return x instanceof Error ? x : new Error(String(x)); } catch { return new Error('unknown'); } };
4132
+
4133
+ const onRej = (reason) => {
4134
+ const e = toErr(reason);
4135
+ const c = this._ctx();
4136
+ print({ type: 'unhandledRejection', name: e.name, message: e.message, stack: e.stack, code: e.code ?? undefined, ...c });
4137
+ };
4138
+ const onExc = (error) => {
4139
+ const e = toErr(error);
4140
+ const c = this._ctx();
4141
+ print({ type: 'uncaughtException', name: e.name, message: e.message, stack: e.stack, code: e.code ?? undefined, ...c });
4142
+ };
4143
+
4144
+ this.global.process.on('unhandledRejection', onRej);
4145
+ this.global.process.on('uncaughtException', onExc);
4146
+ this._cleanup.push(() => {
4147
+ this.global.process.off('unhandledRejection', onRej);
4148
+ this.global.process.off('uncaughtException', onExc);
4149
+ });
4150
+
4151
+ // Signal environment readiness so we can confirm the custom env loaded
4152
+ try { const c = this._ctx(); print({ type: 'envReady', ...c }); } catch {}
4153
+
4154
+ try {
4155
+ const http = this.global.require ? this.global.require('http') : require('http');
4156
+ const originalEmit = http && http.Server && http.Server.prototype && http.Server.prototype.emit;
4157
+ if (originalEmit) {
4158
+ const MAX = 64 * 1024;
4159
+ const asString = (x) => { try { if (typeof x === 'string') return x; if (Buffer.isBuffer(x)) return x.toString('utf8'); return String(x); } catch { return ''; } };
4160
+
4161
+ const patched = function(eventName, req, res) {
4162
+ try {
4163
+ if (eventName === 'request' && req && res && typeof res.write === 'function' && typeof res.end === 'function') {
4164
+ const startAt = Date.now();
4165
+ const safeHeader = (k) => { try { return req && req.headers ? String((req.headers[k.toLowerCase()] ?? '')) : ''; } catch { return ''; } };
4166
+ const reqIdHeader = safeHeader('x-request-id');
4167
+ const chunks = [];
4168
+ const write = res.write.bind(res);
4169
+ const end = res.end.bind(res);
4170
+ const method = req.method ? String(req.method) : undefined;
4171
+ const url = (req.originalUrl || req.url) ? String(req.originalUrl || req.url) : undefined;
4172
+
4173
+ res.write = function(chunk, enc, cb) {
4174
+ try { const s = asString(chunk); if (s) chunks.push(s); } catch {}
4175
+ return write(chunk, enc, cb);
4176
+ };
4177
+ res.end = function(chunk, enc, cb) {
4178
+ try { const s = asString(chunk); if (s) chunks.push(s); } catch {}
4179
+ try {
4180
+ const preview = chunks.join('').slice(0, MAX);
4181
+ const statusCode = typeof res.statusCode === 'number' ? res.statusCode : undefined;
4182
+ const ct = (typeof res.getHeader === 'function' && res.getHeader('content-type')) || undefined;
4183
+ const routePath = (req && req.route && req.route.path) || (req && req.baseUrl && req.path ? (req.baseUrl + req.path) : undefined);
4184
+ const jsonParsed = (() => { try { return JSON.parse(preview); } catch { return undefined; } })();
4185
+ const requestId = reqIdHeader || (jsonParsed && typeof jsonParsed === 'object' ? (jsonParsed.requestId || jsonParsed.reqId || '') : '');
4186
+ const ctx = (global.__JEST_BRIDGE_ENV_REF && global.__JEST_BRIDGE_ENV_REF._ctx) ? global.__JEST_BRIDGE_ENV_REF._ctx() : {};
4187
+ const payload = {
4188
+ type: 'httpResponse',
4189
+ timestampMs: Date.now(),
4190
+ durationMs: Math.max(0, Date.now() - startAt),
4191
+ method, url, statusCode,
4192
+ route: routePath ? String(routePath) : undefined,
4193
+ contentType: ct ? String(ct) : undefined,
4194
+ headers: (typeof res.getHeaders === 'function') ? res.getHeaders() : undefined,
4195
+ requestId: requestId ? String(requestId) : undefined,
4196
+ bodyPreview: preview,
4197
+ json: jsonParsed,
4198
+ testPath: ctx.testPath, currentTestName: ctx.currentTestName,
4199
+ };
4200
+ try {
4201
+ if (!global.__JEST_HTTP_EVENTS__) global.__JEST_HTTP_EVENTS__ = [];
4202
+ const arr = global.__JEST_HTTP_EVENTS__;
4203
+ arr.push({ timestampMs: payload.timestampMs, durationMs: payload.durationMs, method: payload.method, url: payload.url, route: payload.route, statusCode: payload.statusCode, contentType: payload.contentType, requestId: payload.requestId, json: payload.json, bodyPreview: payload.bodyPreview });
4204
+ if (arr.length > 25) arr.splice(0, arr.length - 25);
4205
+ } catch {}
4206
+ try { console.error('[JEST-BRIDGE-EVENT]', JSON.stringify(payload)); } catch {}
4207
+ } catch {}
4208
+ return end(chunk, enc, cb);
4209
+ };
4210
+ try {
4211
+ res.on('close', function onClose() {
4212
+ try {
4213
+ const ended = typeof res.writableEnded === 'boolean' ? res.writableEnded : false;
4214
+ if (!ended) {
4215
+ const routePath = (req && req.route && req.route.path) || (req && req.baseUrl && req.path ? (req.baseUrl + req.path) : undefined);
4216
+ const ctx = (global.__JEST_BRIDGE_ENV_REF && global.__JEST_BRIDGE_ENV_REF._ctx) ? global.__JEST_BRIDGE_ENV_REF._ctx() : {};
4217
+ const payload = {
4218
+ type: 'httpAbort',
4219
+ timestampMs: Date.now(),
4220
+ durationMs: Math.max(0, Date.now() - startAt),
4221
+ method, url,
4222
+ route: routePath ? String(routePath) : undefined,
4223
+ testPath: ctx.testPath, currentTestName: ctx.currentTestName,
4224
+ };
4225
+ try { console.error('[JEST-BRIDGE-EVENT]', JSON.stringify(payload)); } catch {}
4226
+ }
4227
+ } catch {}
4228
+ });
4229
+ } catch {}
4230
+ }
4231
+ } catch {}
4232
+ return originalEmit.apply(this, arguments);
4233
+ };
4234
+
4235
+ try { this.global.__JEST_BRIDGE_ENV_REF = this; } catch {}
4236
+ http.Server.prototype.emit = patched;
4237
+
4238
+ this._cleanup.push(() => {
4239
+ try { if (http.Server && http.Server.prototype) http.Server.prototype.emit = originalEmit; } catch {}
4240
+ try { delete this.global.__JEST_BRIDGE_ENV_REF; } catch {}
4241
+ });
4242
+ }
4243
+ } catch {}
4244
+
4245
+ // Wrap test functions to emit rich assertion events on failures
4246
+ try {
4247
+ const g = this.global;
4248
+ const ctxFn = () => {
4249
+ try {
4250
+ const ref = g.__JEST_BRIDGE_ENV_REF;
4251
+ return ref && typeof ref._ctx === 'function' ? ref._ctx() : {};
4252
+ } catch { return {}; }
4253
+ };
4254
+ const emitAssertion = (err) => {
4255
+ try {
4256
+ const e = toErr(err);
4257
+ const mr = e && typeof e === 'object' && e.matcherResult ? e.matcherResult : undefined;
4258
+ const messageText = (() => { try { return mr && typeof mr.message === 'function' ? String(mr.message()) : (e.message || ''); } catch { return e.message || ''; } })();
4259
+ const expectedPreview = (() => { try { return mr && mr.expected !== undefined ? JSON.stringify(mr.expected, null, 2) : undefined; } catch { return undefined; } })();
4260
+ const actualPreview = (() => { try { return mr && mr.received !== undefined ? JSON.stringify(mr.received, null, 2) : undefined; } catch { return undefined; } })();
4261
+ const c = ctxFn();
4262
+ const expectedRaw = (() => { try { return mr?.expected; } catch { return undefined; } })();
4263
+ const receivedRaw = (() => { try { return mr?.received; } catch { return undefined; } })();
4264
+ print({
4265
+ type: 'assertionFailure',
4266
+ timestampMs: Date.now(),
4267
+ matcher: mr && typeof mr.matcherName === 'string' ? mr.matcherName : undefined,
4268
+ expectedPreview,
4269
+ actualPreview,
4270
+ expectedNumber: typeof expectedRaw === 'number' ? expectedRaw : undefined,
4271
+ receivedNumber: typeof receivedRaw === 'number' ? receivedRaw : undefined,
4272
+ message: messageText,
4273
+ stack: e.stack,
4274
+ ...c,
4275
+ });
4276
+ } catch {}
4277
+ };
4278
+ const wrap = (orig) => {
4279
+ if (!orig || typeof orig !== 'function') return orig;
4280
+ const wrapped = function(name, fn, timeout) {
4281
+ if (typeof fn !== 'function') return orig.call(this, name, fn, timeout);
4282
+ const run = function() {
4283
+ try {
4284
+ const res = fn.apply(this, arguments);
4285
+ if (res && typeof res.then === 'function') {
4286
+ return res.catch((err) => { emitAssertion(err); throw err; });
4287
+ }
4288
+ return res;
4289
+ } catch (err) {
4290
+ emitAssertion(err);
4291
+ throw err;
4292
+ }
4293
+ };
4294
+ return orig.call(this, name, run, timeout);
4295
+ };
4296
+ try { wrapped.only = orig.only && typeof orig.only === 'function' ? wrap(orig.only) : orig.only; } catch {}
4297
+ try { wrapped.skip = orig.skip && typeof orig.skip === 'function' ? wrap(orig.skip) : orig.skip; } catch {}
4298
+ return wrapped;
4299
+ };
4300
+ try { g.it = wrap(g.it); } catch {}
4301
+ try { g.test = wrap(g.test); } catch {}
4302
+ } catch {}
4303
+ }
4304
+
4305
+ async handleTestEvent(evt, state) {
4306
+ if (evt.name === 'test_start') {
4307
+ const store = { testPath: state.testPath, currentTestName: evt.test.name };
4308
+ try { this._als.enterWith(store); } catch {}
4309
+ } else if (evt.name === 'test_done') {
4310
+ try { this._als.enterWith({}); } catch {}
4311
+ try {
4312
+ const events = Array.isArray(global.__JEST_HTTP_EVENTS__) ? global.__JEST_HTTP_EVENTS__ : [];
4313
+ if (events.length) {
4314
+ const batch = events.slice(-10);
4315
+ const payload = { type: 'httpResponseBatch', events: batch, testPath: state.testPath, currentTestName: evt.test.name };
4316
+ try { this.global.console.error('[JEST-BRIDGE-EVENT]', JSON.stringify(payload)); } catch {}
4317
+ try { global.__JEST_HTTP_EVENTS__ = []; } catch {}
4318
+ }
4319
+ } catch {}
4320
+ }
4321
+ }
4322
+
4323
+ async teardown() {
4324
+ for (let i = this._cleanup.length - 1; i >= 0; i--) {
4325
+ try { this._cleanup[i](); } catch {}
4326
+ }
4327
+ await super.teardown();
4328
+ }
4329
+ };
4330
+ `;
4006
4331
 
4007
4332
  // src/lib/stacks.ts
4008
4333
  var isStackLine = (line) => /\s+at\s+/.test(line);
@@ -4065,30 +4390,81 @@ var collapseStacks = (lines) => {
4065
4390
  return out;
4066
4391
  };
4067
4392
 
4068
- // src/lib/jest-bridge.ts
4069
- var extractBridgePath = (raw, cwd) => {
4070
- const matches = Array.from(
4071
- raw.matchAll(/Test results written to:\s+([^\n\r]+jest-bridge-[^\s'"]+\.json)/g)
4072
- );
4073
- if (!matches.length) {
4074
- return null;
4393
+ // src/lib/fp.ts
4394
+ function pipe(initial, ...fns) {
4395
+ return fns.reduce((acc, fn) => fn(acc), initial);
4396
+ }
4397
+ var some = (value) => ({ tag: "Some", value });
4398
+ var none = { tag: "None" };
4399
+ var unfoldr = (initial, step2) => {
4400
+ const out = [];
4401
+ for (let state = initial; ; ) {
4402
+ const result = step2(state);
4403
+ if (result.tag === "None") {
4404
+ break;
4405
+ }
4406
+ const [element, next] = result.value;
4407
+ out.push(element);
4408
+ state = next;
4075
4409
  }
4076
- const jsonPath = matches[matches.length - 1][1].trim().replace(/^["'`]|["'`]$/g, "");
4077
- return path9.isAbsolute(jsonPath) ? jsonPath : path9.resolve(cwd, jsonPath);
4410
+ return out;
4078
4411
  };
4079
- var drawRule = (label) => {
4080
- const width = Math.max(
4081
- 40,
4082
- process.stdout && process.stdout.columns || 80
4083
- );
4084
- if (!label) {
4085
- return ansi.dim("\u2500".repeat(width));
4412
+
4413
+ // src/lib/formatter/parse.ts
4414
+ var isFailureStart = (lineText) => /^\s*●\s+/.test(lineText);
4415
+ var isSuiteLine = (lineText) => /^\s*(PASS|FAIL)\s+/.test(lineText);
4416
+ var isSummaryLine = (lineText) => /^\s*(Test Suites:|Tests:|Snapshots:|Time:|Ran all)/.test(lineText);
4417
+ var collectFailure = (allLines, startIndex) => {
4418
+ const title = stripAnsiSimple(allLines[startIndex]).replace(/^\s*●\s+/, "").trim();
4419
+ const buf = [allLines[startIndex]];
4420
+ let i = startIndex + 1;
4421
+ for (; i < allLines.length; i += 1) {
4422
+ const simple = stripAnsiSimple(allLines[i]);
4423
+ const nextIsStart = isFailureStart(simple) || isSuiteLine(simple) || isSummaryLine(simple);
4424
+ const prevBlank = stripAnsiSimple(allLines[i - 1] ?? "").trim() === "";
4425
+ if (nextIsStart && prevBlank) {
4426
+ break;
4427
+ }
4428
+ buf.push(allLines[i]);
4086
4429
  }
4087
- const plain = stripAnsiSimple(label);
4088
- const pad = Math.max(1, width - plain.length - 1);
4089
- return `${ansi.dim("\u2500".repeat(pad))} ${label}`;
4430
+ return [{ tag: "FailureBlock", title, lines: buf }, i];
4431
+ };
4432
+ var parseSuite = (lineText) => {
4433
+ const match = lineText.match(/^\s*(PASS|FAIL)\s+(.+)$/);
4434
+ return { tag: "PassFail", badge: match[1], rel: match[2] };
4435
+ };
4436
+ var parseChunks = (raw) => {
4437
+ const lines = raw.split(/\r?\n/);
4438
+ return unfoldr({ index: 0 }, (state) => {
4439
+ if (state.index >= lines.length) {
4440
+ return none;
4441
+ }
4442
+ const line = lines[state.index];
4443
+ const simple = stripAnsiSimple(line);
4444
+ if (isFailureStart(simple)) {
4445
+ const [chunk, next] = collectFailure(lines, state.index);
4446
+ return some([chunk, { index: next }]);
4447
+ }
4448
+ if (isSuiteLine(simple)) {
4449
+ return some([parseSuite(simple), { index: state.index + 1 }]);
4450
+ }
4451
+ if (isSummaryLine(simple)) {
4452
+ return some([{ tag: "Summary", line }, { index: state.index + 1 }]);
4453
+ }
4454
+ if (isStackLine(simple)) {
4455
+ return some([{ tag: "Stack", line }, { index: state.index + 1 }]);
4456
+ }
4457
+ return some([{ tag: "Other", line }, { index: state.index + 1 }]);
4458
+ });
4090
4459
  };
4091
- var env = process.env;
4460
+
4461
+ // src/lib/formatter/render.ts
4462
+ var path9 = __toESM(require("node:path"), 1);
4463
+
4464
+ // src/lib/formatter/fns.ts
4465
+ var fs4 = __toESM(require("node:fs"), 1);
4466
+ var util = __toESM(require("node:util"), 1);
4467
+ var import_json5 = __toESM(require_lib(), 1);
4092
4468
  var colorTokens = {
4093
4469
  pass: Colors.Success,
4094
4470
  fail: Colors.Failure,
@@ -4099,31 +4475,17 @@ var colorTokens = {
4099
4475
  failPill: (text) => BackgroundColors.Failure(ansi.white(` ${text} `)),
4100
4476
  runPill: (text) => BackgroundColors.Run(ansi.white(` ${text} `))
4101
4477
  };
4102
- var MAX_CONSOLE_ERRORS_TO_SHOW = 3;
4103
- var isArrayOfPrimitives = (value) => Array.isArray(value) && value.every(
4104
- (element) => ["string", "number", "boolean"].includes(typeof element) || element === null
4105
- );
4106
- var indentBlock = (text, pad = " ") => text.split("\n").map((line) => line ? pad + line : pad.trimEnd()).join("\n");
4107
- var normalizeBlock = (raw) => raw.replace(/^\s*Array\s*\[/, "[").replace(/^\s*Object\s*\{/, "{").replace(/,(\s*[\]}])/g, "$1");
4108
- var stringifyPrettierish = (value) => {
4109
- if (typeof value === "string") {
4110
- const text = normalizeBlock(value.trim());
4111
- if (/^[[{]/.test(text)) {
4112
- try {
4113
- const parsed = import_json5.default.parse(text);
4114
- return JSON.stringify(parsed, null, 2);
4115
- } catch {
4116
- }
4117
- }
4118
- return value;
4119
- }
4120
- if (Array.isArray(value) || value && typeof value === "object") {
4121
- try {
4122
- return JSON.stringify(value, null, 2);
4123
- } catch {
4124
- }
4478
+ var drawRule = (label) => {
4479
+ const width = Math.max(
4480
+ 40,
4481
+ process.stdout && process.stdout.columns || 80
4482
+ );
4483
+ if (!label) {
4484
+ return ansi.dim("\u2500".repeat(width));
4125
4485
  }
4126
- return util.inspect(value, { depth: 10, breakLength: Infinity, compact: false, sorted: true });
4486
+ const plain = stripAnsiSimple(label);
4487
+ const pad = Math.max(1, width - plain.length - 1);
4488
+ return `${ansi.dim("\u2500".repeat(pad))} ${label}`;
4127
4489
  };
4128
4490
  var drawFailLine = () => {
4129
4491
  const width = Math.max(
@@ -4133,9 +4495,29 @@ var drawFailLine = () => {
4133
4495
  return colorTokens.fail("\u2500".repeat(width));
4134
4496
  };
4135
4497
  var renderRunLine = (cwd) => `${colorTokens.runPill("RUN")} ${ansi.dim(cwd.replace(/\\/g, "/"))}`;
4498
+ var buildFileBadgeLine = (rel, failedCount) => failedCount > 0 ? `${colorTokens.failPill("FAIL")} ${ansi.white(rel)}` : `${colorTokens.passPill("PASS")} ${ansi.white(rel)}`;
4499
+ var buildPerFileOverview = (rel, assertions) => {
4500
+ const out = [];
4501
+ out.push(`${ansi.magenta(rel)} ${ansi.dim(`(${assertions.length})`)}`);
4502
+ for (const assertion of assertions) {
4503
+ const name = assertion.fullName;
4504
+ if (assertion.status === "passed") {
4505
+ out.push(` ${colorTokens.pass("\u2713")} ${ansi.dim(name)}`);
4506
+ } else if (assertion.status === "todo") {
4507
+ out.push(` ${colorTokens.todo("\u2610")} ${ansi.dim(name)} ${colorTokens.todo("[todo]")}`);
4508
+ } else if (assertion.status === "pending") {
4509
+ out.push(` ${colorTokens.skip("\u2193")} ${ansi.dim(name)} ${colorTokens.skip("[skipped]")}`);
4510
+ } else {
4511
+ out.push(` ${colorTokens.fail("\xD7")} ${ansi.white(name)}`);
4512
+ }
4513
+ }
4514
+ out.push("");
4515
+ return out;
4516
+ };
4517
+ var isObjectRecord = (value) => typeof value === "object" && value !== null;
4136
4518
  var colorStackLine = (line, projectHint) => {
4137
4519
  const plainLine = stripAnsiSimple(line);
4138
- if (!isStackLine(plainLine)) {
4520
+ if (!/\s+at\s+/.test(plainLine)) {
4139
4521
  return plainLine;
4140
4522
  }
4141
4523
  const match = plainLine.match(/\(?([^\s()]+):(\d+):(\d+)\)?$/);
@@ -4151,298 +4533,144 @@ var colorStackLine = (line, projectHint) => {
4151
4533
  `(${coloredPath}${ansi.dim(":")}${ansi.white(`${lineNumber}:${columnNumber}`)})`
4152
4534
  );
4153
4535
  };
4154
- var renderCodeFrame = (lines, start) => {
4155
- const out = [];
4156
- for (let i = start; i < lines.length; i += 1) {
4157
- const raw = stripAnsiSimple(lines[i]);
4158
- if (!raw.trim()) {
4159
- break;
4160
- }
4161
- if (/^\s*\^+\s*$/.test(raw)) {
4162
- out.push(` ${colorTokens.fail(raw.trimEnd())}`);
4163
- continue;
4164
- }
4165
- const pointerMatch = raw.match(/^\s*>(\s*\d+)\s*\|\s?(.*)$/);
4166
- if (pointerMatch) {
4167
- const num = ansi.dim(pointerMatch[1].trim());
4168
- const code = ansi.yellow(pointerMatch[2] ?? "");
4169
- out.push(` ${colorTokens.fail(">")} ${num} ${ansi.dim("|")} ${code}`);
4170
- continue;
4171
- }
4172
- const normalMatch = raw.match(/^\s*(\d+)\s*\|\s?(.*)$/);
4173
- if (normalMatch) {
4174
- const num = ansi.dim(normalMatch[1]);
4175
- const code = ansi.dim(normalMatch[2] ?? "");
4176
- out.push(` ${num} ${ansi.dim("|")} ${code}`);
4177
- continue;
4178
- }
4179
- out.push(` ${raw}`);
4536
+ var extractBridgePath = (raw, cwd) => {
4537
+ const re = /Test results written to:\s+([^\n\r]+jest-bridge-[^\s'"]+\.json)/g;
4538
+ const matches = Array.from(raw.matchAll(re));
4539
+ if (matches.length === 0) {
4540
+ return null;
4180
4541
  }
4181
- return out;
4542
+ const jsonPath = (matches[matches.length - 1][1] ?? "").trim().replace(/^["'`]|["'`]$/g, "");
4543
+ return /^\//.test(jsonPath) ? jsonPath : `${cwd.replace(/\\/g, "/")}/${jsonPath}`;
4182
4544
  };
4545
+ var findCodeFrameStart = (lines) => lines.findIndex((line) => /^\s*(>?\s*\d+\s*\|)/.test(stripAnsiSimple(line)));
4183
4546
  var _sourceCache = /* @__PURE__ */ new Map();
4184
4547
  var readSource = (file) => {
4185
- const normalizedFile = file.replace(/\\/g, "/");
4186
- const cached = _sourceCache.get(normalizedFile);
4187
- if (cached) {
4188
- return cached;
4548
+ const normalized = file.replace(/\\/g, "/");
4549
+ const hit = _sourceCache.get(normalized);
4550
+ if (hit) {
4551
+ return hit;
4189
4552
  }
4190
4553
  try {
4191
- const txt = fs4.readFileSync(normalizedFile, "utf8");
4192
- const arr = txt.split(/\r?\n/);
4193
- _sourceCache.set(normalizedFile, arr);
4554
+ const arr = fs4.readFileSync(normalized, "utf8").split(/\r?\n/);
4555
+ _sourceCache.set(normalized, arr);
4194
4556
  return arr;
4195
4557
  } catch {
4196
4558
  return [];
4197
4559
  }
4198
4560
  };
4561
+ var renderInlineCodeFrame = (lines, start) => {
4562
+ const out = [];
4563
+ for (let i = start; i < lines.length; i += 1) {
4564
+ const raw = stripAnsiSimple(lines[i]);
4565
+ if (!raw.trim()) {
4566
+ break;
4567
+ }
4568
+ if (/^\s*\^+\s*$/.test(raw)) {
4569
+ out.push(` ${Colors.Failure(raw.trimEnd())}`);
4570
+ } else {
4571
+ const ptr = raw.match(/^\s*>(\s*\d+)\s*\|\s?(.*)$/);
4572
+ if (ptr) {
4573
+ const num = ansi.dim(ptr[1].trim());
4574
+ const code = ansi.yellow(ptr[2] ?? "");
4575
+ out.push(` ${Colors.Failure(">")} ${num} ${ansi.dim("|")} ${code}`);
4576
+ } else {
4577
+ const nor = raw.match(/^\s*(\d+)\s*\|\s?(.*)$/);
4578
+ if (nor) {
4579
+ const num = ansi.dim(nor[1]);
4580
+ const code = ansi.dim(nor[2] ?? "");
4581
+ out.push(` ${num} ${ansi.dim("|")} ${code}`);
4582
+ } else {
4583
+ out.push(` ${raw}`);
4584
+ }
4585
+ }
4586
+ }
4587
+ }
4588
+ return out;
4589
+ };
4199
4590
  var renderSourceCodeFrame = (file, line, context = 3) => {
4200
4591
  const lines = readSource(file);
4201
- if (!lines.length || !Number.isFinite(line)) {
4592
+ if (lines.length === 0 || !Number.isFinite(line)) {
4202
4593
  return [];
4203
4594
  }
4204
4595
  const idx = Math.max(1, Math.min(line, lines.length));
4205
4596
  const start = Math.max(1, idx - context);
4206
4597
  const end = Math.min(lines.length, idx + context);
4207
4598
  const out = [];
4208
- for (let currentLineNumber = start; currentLineNumber <= end; currentLineNumber += 1) {
4209
- const num = ansi.dim(String(currentLineNumber));
4210
- const code = currentLineNumber === idx ? ansi.yellow(lines[currentLineNumber - 1] ?? "") : ansi.dim(lines[currentLineNumber - 1] ?? "");
4211
- if (currentLineNumber === idx) {
4212
- out.push(` ${colorTokens.fail(">")} ${num} ${ansi.dim("|")} ${code}`);
4599
+ for (let current = start; current <= end; current += 1) {
4600
+ const num = ansi.dim(String(current));
4601
+ const code = current === idx ? ansi.yellow(lines[current - 1] ?? "") : ansi.dim(lines[current - 1] ?? "");
4602
+ if (current === idx) {
4603
+ out.push(` ${Colors.Failure(">")} ${num} ${ansi.dim("|")} ${code}`);
4213
4604
  } else {
4214
4605
  out.push(` ${num} ${ansi.dim("|")} ${code}`);
4215
4606
  }
4216
4607
  }
4217
- out.push(` ${colorTokens.fail("^")}`);
4608
+ out.push(` ${Colors.Failure("^")}`);
4218
4609
  return out;
4219
4610
  };
4220
- var findLastProjectFrameIndex = (lines, projectHint) => {
4221
- for (let i = lines.length - 1; i >= 0; i -= 1) {
4222
- const simple = stripAnsiSimple(lines[i]);
4223
- if (isStackLine(simple) && projectHint.test(simple) && !/node_modules|vitest|jest/.test(simple)) {
4224
- return i;
4225
- }
4611
+ var stackLocation = (line) => {
4612
+ const match = stripAnsiSimple(line).match(/\(?([^\s()]+):(\d+):\d+\)?$/);
4613
+ if (!match) {
4614
+ return null;
4226
4615
  }
4227
- return -1;
4616
+ return { file: match[1].replace(/\\/g, "/"), line: Number(match[2]) };
4228
4617
  };
4229
- var renderStackTail = (lines, projectHint, max = 4) => {
4230
- const onlyStack = lines.filter((candidateLine) => isStackLine(stripAnsiSimple(candidateLine)));
4231
- if (!onlyStack.length) {
4232
- return [];
4618
+ var deepestProjectLoc = (stackLines, projectHint) => {
4619
+ for (let i = stackLines.length - 1; i >= 0; i -= 1) {
4620
+ const simple = stripAnsiSimple(stackLines[i]);
4621
+ if (isStackLine(simple) && projectHint.test(simple) && !/node_modules|vitest|jest/.test(simple)) {
4622
+ return stackLocation(stackLines[i]);
4623
+ }
4233
4624
  }
4234
- const tail = onlyStack.slice(-max);
4235
- return tail.map((frameLine) => ` ${colorStackLine(frameLine, projectHint)}`);
4236
- };
4237
- var firstProjectFrames = (lines, projectHint, max = 2) => {
4238
- const onlyStack = lines.filter((ln) => isStackLine(stripAnsiSimple(ln)));
4239
- const projectOnly = onlyStack.filter((ln) => projectHint.test(stripAnsiSimple(ln)));
4240
- return projectOnly.slice(0, max).map((ln) => ` ${colorStackLine(ln, projectHint)}`);
4625
+ return null;
4241
4626
  };
4242
- var isTerminator = (lineText) => !lineText.trim() || isStackLine(lineText);
4243
- var extractAssertionMessage = (msgLines) => {
4244
- const lines = msgLines.map((rawLine) => stripAnsiSimple(rawLine));
4627
+ var buildCodeFrameSection = (messageLines, ctx, synthLoc) => {
4245
4628
  const out = [];
4246
- const hintIdx = lines.findIndex(
4247
- (candidateLine) => /expect\(.+?\)\.(?:to|not\.)/.test(candidateLine) || /\b(?:AssertionError|Error):/.test(candidateLine)
4248
- );
4249
- if (hintIdx >= 0) {
4250
- out.push(lines[hintIdx]);
4251
- }
4252
- const collectBlock = (start) => {
4253
- out.push(lines[start]);
4254
- for (let i = start + 1; i < lines.length; i += 1) {
4255
- const candidate = lines[i];
4256
- if (isTerminator(candidate)) {
4257
- break;
4258
- }
4259
- out.push(candidate);
4260
- }
4261
- };
4262
- const expectedIdx = lines.findIndex(
4263
- (candidateLine) => /^\s*Expected:/.test(candidateLine)
4264
- );
4265
- const receivedIdx = lines.findIndex(
4266
- (candidateLine) => /^\s*Received:/.test(candidateLine)
4267
- );
4268
- const diffIdx = lines.findIndex(
4269
- (candidateLine) => /^\s*(?:- Expected|\+ Received|Difference:)/.test(candidateLine)
4270
- );
4271
- if (expectedIdx >= 0) {
4272
- collectBlock(expectedIdx);
4273
- }
4274
- if (receivedIdx >= 0) {
4275
- collectBlock(receivedIdx);
4276
- }
4277
- if (diffIdx >= 0) {
4278
- collectBlock(diffIdx);
4629
+ const start = findCodeFrameStart(messageLines);
4630
+ if (start >= 0) {
4631
+ out.push(...renderInlineCodeFrame(messageLines, start), "");
4632
+ return out;
4279
4633
  }
4280
- if (out.length === 0 && hintIdx >= 0) {
4281
- for (let i = hintIdx + 1; i < lines.length && out.length < 4; i += 1) {
4282
- const candidate = lines[i];
4283
- if (isTerminator(candidate)) {
4284
- break;
4285
- }
4286
- out.push(candidate);
4287
- }
4634
+ if (ctx.showStacks && synthLoc) {
4635
+ out.push(...renderSourceCodeFrame(synthLoc.file, synthLoc.line), "");
4288
4636
  }
4289
4637
  return out;
4290
4638
  };
4291
- var stackLocation = (line) => {
4292
- const match = stripAnsiSimple(line).match(/\(?([^\s()]+):(\d+):\d+\)?$/);
4293
- return match ? { file: match[1].replace(/\\/g, "/"), line: Number(match[2]) } : null;
4294
- };
4295
- var JEST_BRIDGE_REPORTER_SOURCE = `const fs = require('fs');
4296
- const path = require('path');
4297
-
4298
- class BridgeReporter {
4299
- constructor(globalConfig, options) {
4300
- this.out = process.env.JEST_BRIDGE_OUT || (options && options.outFile) || path.join(process.cwd(), 'coverage', 'jest-run.json');
4301
- this.buf = { startTime: Date.now(), testResults: [], aggregated: null };
4302
- }
4303
- onRunStart() { this.buf.startTime = Date.now(); }
4304
- onTestResult(_test, tr) {
4305
- const mapAssertion = (a) => ({
4306
- title: a.title,
4307
- fullName: a.fullName || [...(a.ancestorTitles || []), a.title].join(' '),
4308
- status: a.status,
4309
- duration: a.duration || 0,
4310
- location: a.location || null,
4311
- failureMessages: (a.failureMessages || []).map(String),
4312
- });
4313
- this.buf.testResults.push({
4314
- testFilePath: tr.testFilePath,
4315
- status: tr.numFailingTests ? 'failed' : 'passed',
4316
- failureMessage: tr.failureMessage || '',
4317
- failureDetails: tr.failureDetails || [],
4318
- console: tr.console || null,
4319
- perfStats: tr.perfStats || {},
4320
- testResults: (tr.testResults || []).map(mapAssertion),
4321
- });
4322
- }
4323
- onRunComplete(_contexts, agg) {
4324
- this.buf.aggregated = {
4325
- numTotalTestSuites: agg.numTotalTestSuites,
4326
- numPassedTestSuites: agg.numPassedTestSuites,
4327
- numFailedTestSuites: agg.numFailedTestSuites,
4328
- numTotalTests: agg.numTotalTests,
4329
- numPassedTests: agg.numPassedTests,
4330
- numFailedTests: agg.numFailedTests,
4331
- numPendingTests: agg.numPendingTests,
4332
- numTodoTests: agg.numTodoTests,
4333
- startTime: agg.startTime,
4334
- success: agg.success,
4335
- runTimeMs: agg.testResults.reduce((t, r) => t + Math.max(0, (r.perfStats?.end || 0) - (r.perfStats?.start || 0)), 0),
4336
- };
4337
- fs.mkdirSync(path.dirname(this.out), { recursive: true });
4338
- fs.writeFileSync(this.out, JSON.stringify(this.buf), 'utf8');
4339
- }
4340
- }
4341
- module.exports = BridgeReporter;`;
4342
- var asDict = (value) => value && typeof value === "object" ? value : null;
4343
- var get = (objectValue, key) => objectValue ? objectValue[key] : void 0;
4344
- var getStr = (objectValue, key) => {
4345
- const candidate = get(objectValue, key);
4346
- return typeof candidate === "string" ? candidate : void 0;
4347
- };
4348
- function linesFromDetails(details) {
4349
- const stacks = [];
4350
- const messages = [];
4351
- if (!details) {
4352
- return { stacks, messages };
4353
- }
4354
- const pushMaybe = (value, bucket) => {
4355
- if (typeof value === "string" && value.trim()) {
4356
- bucket.push(...value.split(/\r?\n/));
4357
- }
4358
- };
4359
- for (const detail of details) {
4360
- if (typeof detail === "string") {
4361
- if (/\s+at\s.+\(.+:\d+:\d+\)/.test(detail)) {
4362
- pushMaybe(detail, stacks);
4363
- } else {
4364
- pushMaybe(detail, messages);
4365
- }
4366
- continue;
4367
- }
4368
- const dict = asDict(detail);
4369
- if (dict) {
4370
- pushMaybe(getStr(dict, "stack"), stacks);
4371
- pushMaybe(getStr(dict, "message"), messages);
4372
- const err = asDict(get(dict, "error"));
4373
- pushMaybe(getStr(err, "stack"), stacks);
4374
- pushMaybe(getStr(err, "message"), messages);
4375
- const matcherResult = asDict(get(dict, "matcherResult"));
4376
- pushMaybe(getStr(matcherResult, "stack"), stacks);
4377
- pushMaybe(getStr(matcherResult, "message"), messages);
4378
- pushMaybe(getStr(matcherResult, "expected"), messages);
4379
- pushMaybe(getStr(matcherResult, "received"), messages);
4380
- }
4381
- }
4382
- return { stacks, messages };
4383
- }
4384
- function labelForMessage(lines) {
4385
- const joined = lines.join(" ");
4386
- const matched = joined.match(/\b(TypeError|ReferenceError|SyntaxError|RangeError|AssertionError)\b/) || joined.match(/\bError\b/);
4387
- if (matched) {
4388
- const typeName = matched[1] ?? "Error";
4389
- return `${typeName}:`;
4390
- }
4391
- return /expect\(.+?\)\.(?:to|not\.)/.test(joined) ? "Assertion:" : "Message:";
4392
- }
4393
- function extractExpectedReceived(details, lines) {
4394
- if (details) {
4395
- for (const detail of details) {
4396
- const dict = asDict(detail);
4397
- const matcherResult = dict && asDict(get(dict, "matcherResult"));
4398
- if (matcherResult) {
4399
- const expected = get(matcherResult, "expected");
4400
- const received = get(matcherResult, "received");
4401
- if (expected !== void 0 || received !== void 0) {
4402
- return { expected, received };
4403
- }
4404
- }
4639
+ var normalizeBlock = (raw) => raw.replace(/^\s*Array\s*\[/, "[").replace(/^\s*Object\s*\{/, "{").replace(/,(\s*[\]}])/g, "$1");
4640
+ var stringifyPrettierish = (value) => {
4641
+ if (typeof value === "string") {
4642
+ const text = normalizeBlock(value.trim());
4643
+ try {
4644
+ const parsed = import_json5.default.parse(text);
4645
+ return JSON.stringify(parsed, null, 2);
4646
+ } catch {
4647
+ return value;
4405
4648
  }
4406
4649
  }
4407
- if (lines && lines.length) {
4408
- const expectedLines = [];
4409
- const receivedLines = [];
4410
- let mode = "none";
4411
- for (const rawLine of lines) {
4412
- const simple = stripAnsiSimple(rawLine);
4413
- if (/^\s*Expected:/.test(simple)) {
4414
- mode = "exp";
4415
- expectedLines.push(simple.replace(/^\s*Expected:\s*/, ""));
4416
- continue;
4417
- }
4418
- if (/^\s*Received:/.test(simple)) {
4419
- mode = "rec";
4420
- receivedLines.push(simple.replace(/^\s*Received:\s*/, ""));
4421
- continue;
4422
- }
4423
- if (/^\s*[-+]\s/.test(simple)) {
4424
- continue;
4425
- }
4426
- if (!simple.trim()) {
4427
- mode = "none";
4428
- } else if (mode === "exp") {
4429
- expectedLines.push(simple);
4430
- } else if (mode === "rec") {
4431
- receivedLines.push(simple);
4432
- }
4433
- }
4434
- if (expectedLines.length || receivedLines.length) {
4435
- return { expected: expectedLines.join("\n"), received: receivedLines.join("\n") };
4436
- }
4437
- const unified = extractFromUnifiedDiff(lines);
4438
- if (unified.expected !== void 0 || unified.received !== void 0) {
4439
- return unified;
4650
+ if (Array.isArray(value) || isObjectRecord(value)) {
4651
+ try {
4652
+ return JSON.stringify(value, null, 2);
4653
+ } catch {
4654
+ return util.inspect(value, {
4655
+ depth: 10,
4656
+ breakLength: Infinity,
4657
+ compact: false,
4658
+ sorted: true
4659
+ });
4440
4660
  }
4441
4661
  }
4442
- return {};
4443
- }
4444
- function extractFromUnifiedDiff(rawLines) {
4445
- const lines = rawLines.map((lineText) => stripAnsiSimple(lineText));
4662
+ return util.inspect(value, {
4663
+ depth: 10,
4664
+ breakLength: Infinity,
4665
+ compact: false,
4666
+ sorted: true
4667
+ });
4668
+ };
4669
+ var isArrayOfPrimitives = (value) => Array.isArray(value) && value.every(
4670
+ (element) => ["string", "number", "boolean"].includes(typeof element) || element === null
4671
+ );
4672
+ var extractFromUnifiedDiff = (rawLines) => {
4673
+ const lines = (rawLines ?? []).map((lineText) => stripAnsiSimple(lineText));
4446
4674
  let startIndex = -1;
4447
4675
  for (let i = 0; i < lines.length; i += 1) {
4448
4676
  const lt = lines[i];
@@ -4489,12 +4717,10 @@ function extractFromUnifiedDiff(rawLines) {
4489
4717
  receivedParts.push(unsigned);
4490
4718
  }
4491
4719
  if (!expDone && expectedParts.length > 0) {
4492
- const expJoined = expectedParts.join("\n");
4493
- expDone = canParseJsonish(expJoined);
4720
+ expDone = canParseJsonish(expectedParts.join("\n"));
4494
4721
  }
4495
4722
  if (!recDone && receivedParts.length > 0) {
4496
- const recJoined = receivedParts.join("\n");
4497
- recDone = canParseJsonish(recJoined);
4723
+ recDone = canParseJsonish(receivedParts.join("\n"));
4498
4724
  }
4499
4725
  if (opened && expDone && recDone) {
4500
4726
  break;
@@ -4524,32 +4750,108 @@ function extractFromUnifiedDiff(rawLines) {
4524
4750
  result.received = recStr;
4525
4751
  }
4526
4752
  return result;
4527
- }
4528
- function renderPrettyDiff(payload) {
4529
- const out = [];
4530
- const { expected, received } = payload;
4531
- if (expected === void 0 && received === void 0) {
4532
- return out;
4753
+ };
4754
+ var extractExpectedReceived = (details, lines) => {
4755
+ if (details) {
4756
+ for (const detail of details) {
4757
+ const dict = isObjectRecord(detail) ? detail : void 0;
4758
+ const matcher = dict && isObjectRecord(dict.matcherResult) ? dict.matcherResult : void 0;
4759
+ if (matcher) {
4760
+ const expectedValue = matcher.expected;
4761
+ const receivedValue = matcher.received;
4762
+ const matcherName = String(
4763
+ matcher.matcherName || ""
4764
+ );
4765
+ if (matcherName === "toHaveBeenCalledTimes" || matcherName === "toBeCalledTimes") {
4766
+ const getCallsCount = (actual) => {
4767
+ if (isObjectRecord(actual) && Array.isArray(actual.calls)) {
4768
+ return actual.calls.length;
4769
+ }
4770
+ if (typeof actual === "number") {
4771
+ return actual;
4772
+ }
4773
+ if (Array.isArray(actual)) {
4774
+ return actual.length;
4775
+ }
4776
+ return void 0;
4777
+ };
4778
+ const expectedNumber = getCallsCount(expectedValue);
4779
+ const actualValue = matcher.actual ?? receivedValue;
4780
+ const receivedNumber = getCallsCount(actualValue);
4781
+ if (expectedNumber !== void 0 || receivedNumber !== void 0) {
4782
+ return { expected: expectedNumber, received: receivedNumber };
4783
+ }
4784
+ }
4785
+ if (expectedValue !== void 0 && receivedValue !== void 0) {
4786
+ return { expected: expectedValue, received: receivedValue };
4787
+ }
4788
+ }
4789
+ }
4790
+ }
4791
+ if (lines && lines.length) {
4792
+ const expectedLines = [];
4793
+ const receivedLines = [];
4794
+ let mode = "none";
4795
+ for (const rawLine of lines) {
4796
+ const simple = stripAnsiSimple(rawLine);
4797
+ if (/^\s*Expected:/.test(simple)) {
4798
+ mode = "exp";
4799
+ expectedLines.push(simple.replace(/^\s*Expected:\s*/, ""));
4800
+ continue;
4801
+ }
4802
+ if (/^\s*Received:/.test(simple)) {
4803
+ mode = "rec";
4804
+ receivedLines.push(simple.replace(/^\s*Received:\s*/, ""));
4805
+ continue;
4806
+ }
4807
+ if (/^\s*[-+]\s/.test(simple)) {
4808
+ continue;
4809
+ }
4810
+ if (!simple.trim()) {
4811
+ mode = "none";
4812
+ } else if (mode === "exp") {
4813
+ expectedLines.push(simple);
4814
+ } else if (mode === "rec") {
4815
+ receivedLines.push(simple);
4816
+ }
4817
+ }
4818
+ if (expectedLines.length || receivedLines.length) {
4819
+ return { expected: expectedLines.join("\n"), received: receivedLines.join("\n") };
4820
+ }
4821
+ const unified = extractFromUnifiedDiff(lines);
4822
+ if (unified.expected !== void 0 || unified.received !== void 0) {
4823
+ return unified;
4824
+ }
4533
4825
  }
4534
- const expectedString = stringifyPrettierish(expected);
4535
- const receivedString = stringifyPrettierish(received);
4826
+ return {};
4827
+ };
4828
+ var buildPrettyDiffSection = (details, messageLines) => {
4829
+ const payload = extractExpectedReceived(details, messageLines);
4830
+ if (payload.expected === void 0 && payload.received === void 0) {
4831
+ return [];
4832
+ }
4833
+ const expectedString = stringifyPrettierish(payload.expected);
4834
+ const receivedString = stringifyPrettierish(payload.received);
4835
+ const out = [];
4836
+ const expectedLenLabel = Array.isArray(payload.expected) ? ansi.dim(` (len ${payload.expected.length})`) : "";
4837
+ out.push(` ${ansi.bold("Expected")}${expectedLenLabel}`);
4536
4838
  out.push(
4537
- ` ${ansi.bold("Expected")} ${ansi.dim(
4538
- expected && Array.isArray(expected) ? `(len ${expected.length})` : ""
4539
- )}`
4839
+ expectedString.split("\n").map((expectedLine) => ` ${Colors.Success(expectedLine)}`).join("\n")
4540
4840
  );
4541
- out.push(indentBlock(colorTokens.pass(expectedString)));
4841
+ const receivedLenLabel = Array.isArray(payload.received) ? ansi.dim(` (len ${payload.received.length})`) : "";
4842
+ out.push(` ${ansi.bold("Received")}${receivedLenLabel}`);
4542
4843
  out.push(
4543
- ` ${ansi.bold("Received")} ${ansi.dim(
4544
- received && Array.isArray(received) ? `(len ${received.length})` : ""
4545
- )}`
4844
+ receivedString.split("\n").map((receivedLine) => ` ${Colors.Failure(receivedLine)}`).join("\n")
4546
4845
  );
4547
- out.push(indentBlock(colorTokens.fail(receivedString)));
4548
- if (isArrayOfPrimitives(expected) && isArrayOfPrimitives(received)) {
4549
- const expectedSet = new Set(expected.map((element) => String(element)));
4550
- const receivedSet = new Set(received.map((element) => String(element)));
4551
- const missing = [...expectedSet].filter((element) => !receivedSet.has(element));
4552
- const unexpected = [...receivedSet].filter((element) => !expectedSet.has(element));
4846
+ if (isArrayOfPrimitives(payload.expected) && isArrayOfPrimitives(payload.received)) {
4847
+ const expectedSet = new Set(
4848
+ payload.expected.map((element) => String(element))
4849
+ );
4850
+ const receivedSet = new Set(
4851
+ payload.received.map((element) => String(element))
4852
+ );
4853
+ const missing = Array.from(expectedSet).filter((element) => !receivedSet.has(element));
4854
+ const unexpected = Array.from(receivedSet).filter((element) => !expectedSet.has(element));
4553
4855
  const parts = [];
4554
4856
  if (missing.length) {
4555
4857
  parts.push(
@@ -4562,67 +4864,85 @@ function renderPrettyDiff(payload) {
4562
4864
  );
4563
4865
  }
4564
4866
  if (parts.length) {
4565
- out.push(` ${ansi.dim("Difference:")} ${colorTokens.fail(parts.join(ansi.dim(" | ")))}`);
4867
+ out.push(` ${ansi.dim("Difference:")} ${Colors.Failure(parts.join(ansi.dim(" | ")))}`);
4566
4868
  }
4567
4869
  }
4568
4870
  out.push("");
4569
4871
  return out;
4570
- }
4571
- function pickPrimaryMessage(candidateMessageLines, details) {
4572
- const extracted = extractAssertionMessage(candidateMessageLines);
4573
- if (extracted.length) {
4574
- return extracted;
4575
- }
4576
- const errorLine = details.messages.find(
4577
- (lineText) => /\b(?:Error|TypeError|ReferenceError|SyntaxError|RangeError|AssertionError)\b/.test(lineText)
4872
+ };
4873
+ var buildMessageSection = (messageLines, details, _ctx, opts) => {
4874
+ const out = [];
4875
+ const lines = messageLines.map((lineText) => stripAnsiSimple(lineText));
4876
+ const hintIdx = lines.findIndex(
4877
+ (candidate) => /expect\(.+?\)\.(?:to|not\.)/.test(candidate) || /\b(?:AssertionError|Error):/.test(candidate)
4578
4878
  );
4579
- if (errorLine) {
4580
- return [errorLine];
4581
- }
4582
- const firstNonEmpty = details.messages.find((lineText) => lineText.trim().length);
4583
- if (firstNonEmpty) {
4584
- return [firstNonEmpty];
4585
- }
4586
- return [];
4587
- }
4588
- function colorUnifiedDiffLine(simple) {
4589
- if (/^\s*-\s/.test(simple)) {
4590
- return colorTokens.fail(simple);
4879
+ const acc = [];
4880
+ if (hintIdx >= 0) {
4881
+ acc.push(lines[hintIdx]);
4591
4882
  }
4592
- if (/^\s*\+\s/.test(simple)) {
4593
- return colorTokens.pass(simple);
4883
+ const pushBlock = (start) => {
4884
+ acc.push(lines[start]);
4885
+ for (let i = start + 1; i < lines.length; i += 1) {
4886
+ const candidate = lines[i];
4887
+ if (!candidate.trim() || isStackLine(candidate)) {
4888
+ break;
4889
+ }
4890
+ acc.push(candidate);
4891
+ }
4892
+ };
4893
+ const expectedIdx = lines.findIndex((lineText) => /^\s*Expected:/.test(lineText));
4894
+ const receivedIdx = lines.findIndex((lineText) => /^\s*Received:/.test(lineText));
4895
+ const diffIdx = lines.findIndex(
4896
+ (lineText) => /^\s*(?:- Expected|\+ Received|Difference:)/.test(lineText)
4897
+ );
4898
+ if (expectedIdx >= 0) {
4899
+ pushBlock(expectedIdx);
4594
4900
  }
4595
- return simple;
4596
- }
4597
- var findCodeFrameStart = (lines) => lines.findIndex((line) => /^\s*(>?\s*\d+\s*\|)/.test(stripAnsiSimple(line)));
4598
- var deepestProjectLoc = (stackLines, projectHint) => {
4599
- const idx = findLastProjectFrameIndex(stackLines, projectHint);
4600
- return idx >= 0 ? stackLocation(stackLines[idx]) : null;
4601
- };
4602
- var buildCodeFrameSection = (messageLines, ctx, synthLoc) => {
4603
- const lines = [];
4604
- const start = findCodeFrameStart(messageLines);
4605
- if (start >= 0) {
4606
- lines.push(...renderCodeFrame(messageLines, start), "");
4607
- return lines;
4901
+ if (receivedIdx >= 0) {
4902
+ pushBlock(receivedIdx);
4608
4903
  }
4609
- if (ctx.showStacks && synthLoc) {
4610
- lines.push(...renderSourceCodeFrame(synthLoc.file, synthLoc.line), "");
4904
+ if (diffIdx >= 0) {
4905
+ pushBlock(diffIdx);
4611
4906
  }
4612
- return lines;
4613
- };
4614
- var buildMessageSection = (messageLines, details, ctx, opts) => {
4615
- const out = [];
4616
- const primary = pickPrimaryMessage(messageLines, details);
4617
- const filtered = opts?.suppressDiff ? primary.filter((raw) => {
4907
+ const filtered = opts?.suppressDiff ? acc.filter((raw) => {
4618
4908
  const simple = stripAnsiSimple(raw);
4619
4909
  return !/^\s*(Expected:|Received:|Difference:)\b/.test(simple) && !/^\s*[-+]\s/.test(simple) && !/^\s*(Array\s*\[|Object\s*\{)/.test(simple);
4620
- }) : primary;
4621
- if (filtered.length) {
4622
- const label = labelForMessage(filtered);
4910
+ }) : acc;
4911
+ const hasOnlyBareError = filtered.length === 0 || filtered.length === 1 && /^\s*(?:Error|AssertionError):?\s*$/.test(filtered[0] ?? "");
4912
+ const fallbackLines = [];
4913
+ if (hasOnlyBareError) {
4914
+ const startFrom = hintIdx >= 0 ? hintIdx + 1 : 0;
4915
+ for (let i = startFrom; i < lines.length; i += 1) {
4916
+ const candidate = lines[i];
4917
+ if (!candidate.trim()) {
4918
+ break;
4919
+ }
4920
+ if (isStackLine(candidate)) {
4921
+ break;
4922
+ }
4923
+ fallbackLines.push(candidate);
4924
+ }
4925
+ if (fallbackLines.length === 0 && details && details.messages && details.messages.length) {
4926
+ fallbackLines.push(
4927
+ ...details.messages.map((messageText) => stripAnsiSimple(messageText)).filter((messageText) => messageText.trim().length > 0).slice(0, 6)
4928
+ );
4929
+ }
4930
+ }
4931
+ if (filtered.length > 0) {
4932
+ const label = (() => {
4933
+ const joined = filtered.join(" ");
4934
+ const matchResult = joined.match(/\b(TypeError|ReferenceError|SyntaxError|RangeError|AssertionError)\b/) || joined.match(/\bError\b/);
4935
+ if (matchResult) {
4936
+ const typeName = matchResult[1] ?? "Error";
4937
+ return `${typeName}:`;
4938
+ }
4939
+ return /expect\(.+?\)\.(?:to|not\.)/.test(joined) ? "Assertion:" : "Message:";
4940
+ })();
4623
4941
  out.push(` ${ansi.bold(label)}`);
4624
- for (const lineText of filtered) {
4625
- out.push(` ${ansi.yellow(colorUnifiedDiffLine(lineText))}`);
4942
+ const body = hasOnlyBareError ? fallbackLines : filtered;
4943
+ for (const lineText of body) {
4944
+ const colored = /^\s*-\s/.test(lineText) ? Colors.Failure(lineText) : /^\s*\+\s/.test(lineText) ? Colors.Success(lineText) : lineText;
4945
+ out.push(` ${ansi.yellow(colored)}`);
4626
4946
  }
4627
4947
  if (opts?.stackPreview && opts.stackPreview.length) {
4628
4948
  for (const frame of opts.stackPreview) {
@@ -4633,32 +4953,85 @@ var buildMessageSection = (messageLines, details, ctx, opts) => {
4633
4953
  }
4634
4954
  return out;
4635
4955
  };
4636
- function isConsoleEntry(candidate) {
4637
- return typeof candidate === "object" && candidate !== null;
4638
- }
4639
- var buildConsoleSection = (maybeConsole) => {
4640
- const out = [];
4641
- if (!Array.isArray(maybeConsole)) {
4642
- return out;
4956
+ var linesFromDetails = (details) => {
4957
+ const stacks = [];
4958
+ const messages = [];
4959
+ if (!details) {
4960
+ return { stacks, messages };
4643
4961
  }
4644
- const entries = maybeConsole.filter(isConsoleEntry);
4645
- const errorsOnly = entries.filter((entry) => {
4646
- const val = entry?.type;
4647
- return String(val ?? "").toLowerCase() === "error";
4648
- });
4649
- const scored = errorsOnly.map((entry) => {
4650
- const raw = entry?.message;
4651
- const msg = Array.isArray(raw) ? raw.map(String).join(" ") : typeof raw === "string" ? raw : String(raw ?? "");
4652
- return { msg, score: msg.length };
4653
- }).filter((item) => item.msg.trim().length > 0).sort((left, right) => right.score - left.score).slice(0, MAX_CONSOLE_ERRORS_TO_SHOW);
4654
- if (scored.length) {
4655
- out.push(ansi.dim(" Console errors:"));
4656
- for (const item of scored) {
4657
- out.push(` ${ansi.dim("\u2022")} ${item.msg}`);
4962
+ const pushMaybe = (value, bucket) => {
4963
+ if (typeof value === "string" && value.trim()) {
4964
+ bucket.push(...value.split(/\r?\n/));
4965
+ }
4966
+ };
4967
+ const visitDeep = (value, depth) => {
4968
+ if (depth > 3 || value == null) {
4969
+ return;
4970
+ }
4971
+ if (typeof value === "string") {
4972
+ pushMaybe(value, messages);
4973
+ return;
4974
+ }
4975
+ if (typeof value !== "object") {
4976
+ return;
4977
+ }
4978
+ const obj = value;
4979
+ if (typeof obj.message === "string") {
4980
+ pushMaybe(obj.message, messages);
4981
+ }
4982
+ if (typeof obj.stack === "string") {
4983
+ pushMaybe(obj.stack, stacks);
4984
+ }
4985
+ if (typeof obj.expected === "string") {
4986
+ pushMaybe(obj.expected, messages);
4987
+ }
4988
+ if (typeof obj.received === "string") {
4989
+ pushMaybe(obj.received, messages);
4990
+ }
4991
+ const arrays = ["errors", "causes", "aggregatedErrors"];
4992
+ for (const key of arrays) {
4993
+ const arr = obj[key];
4994
+ if (Array.isArray(arr)) {
4995
+ for (const element of arr) {
4996
+ visitDeep(element, depth + 1);
4997
+ }
4998
+ }
4999
+ }
5000
+ const nestedCandidates = ["error", "cause", "matcherResult"];
5001
+ for (const key of nestedCandidates) {
5002
+ if (obj[key] && typeof obj[key] === "object") {
5003
+ visitDeep(obj[key], depth + 1);
5004
+ }
5005
+ }
5006
+ };
5007
+ for (const detail of details) {
5008
+ if (typeof detail === "string") {
5009
+ if (/\s+at\s.+\(.+:\d+:\d+\)/.test(detail)) {
5010
+ pushMaybe(detail, stacks);
5011
+ } else {
5012
+ pushMaybe(detail, messages);
5013
+ }
5014
+ } else if (isObjectRecord(detail)) {
5015
+ pushMaybe(detail.stack, stacks);
5016
+ pushMaybe(detail.message, messages);
5017
+ const err = isObjectRecord(detail.error) ? detail.error : void 0;
5018
+ if (err) {
5019
+ pushMaybe(err.stack, stacks);
5020
+ pushMaybe(err.message, messages);
5021
+ }
5022
+ const matcher = isObjectRecord(detail.matcherResult) ? detail.matcherResult : void 0;
5023
+ if (matcher) {
5024
+ pushMaybe(matcher.stack, stacks);
5025
+ pushMaybe(matcher.message, messages);
5026
+ pushMaybe(matcher.expected, messages);
5027
+ pushMaybe(matcher.received, messages);
5028
+ }
5029
+ if (messages.length === 0 && stacks.length === 0) {
5030
+ visitDeep(detail, 0);
5031
+ }
4658
5032
  }
4659
- out.push("");
4660
5033
  }
4661
- return out;
5034
+ return { stacks, messages };
4662
5035
  };
4663
5036
  var buildStackSection = (mergedForStack, ctx, fallbackLoc) => {
4664
5037
  const out = [];
@@ -4667,13 +5040,18 @@ var buildStackSection = (mergedForStack, ctx, fallbackLoc) => {
4667
5040
  out.push(` ${ansi.dim("(hidden by TEST_CLI_STACKS=)")}`, "");
4668
5041
  return out;
4669
5042
  }
4670
- const tail = renderStackTail(mergedForStack, ctx.projectHint, 4);
5043
+ const onlyStack = mergedForStack.filter(
5044
+ (lineText) => isStackLine(stripAnsiSimple(lineText))
5045
+ );
5046
+ const tail = onlyStack.slice(-4);
4671
5047
  if (tail.length) {
4672
- out.push(...tail);
5048
+ for (const frameLine of tail) {
5049
+ out.push(` ${colorStackLine(String(frameLine), ctx.projectHint)}`);
5050
+ }
4673
5051
  const loc = deepestProjectLoc(mergedForStack, ctx.projectHint);
4674
5052
  if (loc) {
4675
5053
  const href = preferredEditorHref(loc.file, loc.line, ctx.editorCmd);
4676
- out.push(` ${ansi.dim("at")} ${osc8(`${path9.basename(loc.file)}:${loc.line}`, href)}`);
5054
+ out.push(` ${ansi.dim("at")} ${osc8(`${loc.file.split("/").pop()}:${loc.line}`, href)}`);
4677
5055
  }
4678
5056
  out.push("");
4679
5057
  return out;
@@ -4688,171 +5066,460 @@ var buildStackSection = (mergedForStack, ctx, fallbackLoc) => {
4688
5066
  out.push(` ${ansi.dim("(no stack provided)")}`, "");
4689
5067
  return out;
4690
5068
  };
4691
- var buildFileBadgeLine = (rel, failedCount) => failedCount > 0 ? `${colorTokens.failPill("FAIL")} ${ansi.white(rel)}` : `${colorTokens.passPill("PASS")} ${ansi.white(rel)}`;
4692
- var buildPerFileOverview = (rel, assertions) => {
5069
+ var MAX_CONSOLE_ERRORS_TO_SHOW = 3;
5070
+ var isConsoleEntry = (candidate) => typeof candidate === "object" && candidate !== null;
5071
+ var buildConsoleSection = (maybeConsole) => {
4693
5072
  const out = [];
4694
- out.push(`${ansi.magenta(rel)} ${ansi.dim(`(${assertions.length})`)}`);
4695
- for (const assertion of assertions) {
4696
- const name = assertion.fullName;
4697
- if (assertion.status === "passed") {
4698
- out.push(` ${colorTokens.pass("\u2713")} ${ansi.dim(name)}`);
4699
- } else if (assertion.status === "todo") {
4700
- out.push(` ${colorTokens.todo("\u2610")} ${ansi.dim(name)} ${colorTokens.todo("[todo]")}`);
4701
- } else if (assertion.status === "pending") {
4702
- out.push(` ${colorTokens.skip("\u2193")} ${ansi.dim(name)} ${colorTokens.skip("[skipped]")}`);
4703
- } else {
4704
- out.push(` ${colorTokens.fail("\xD7")} ${ansi.white(name)}`);
5073
+ if (!Array.isArray(maybeConsole)) {
5074
+ return out;
5075
+ }
5076
+ const entries = maybeConsole.filter(isConsoleEntry);
5077
+ const errorsOnly = entries.filter((entry) => String(entry?.type ?? "").toLowerCase() === "error");
5078
+ const scored = errorsOnly.map((entry) => {
5079
+ const raw = entry?.message;
5080
+ const msg = Array.isArray(raw) ? raw.map(String).join(" ") : typeof raw === "string" ? raw : String(raw ?? "");
5081
+ return { msg, score: msg.length };
5082
+ }).filter((item) => item.msg.trim().length > 0).sort((left, right) => right.score - left.score).slice(0, MAX_CONSOLE_ERRORS_TO_SHOW);
5083
+ if (scored.length) {
5084
+ out.push(ansi.dim(" Console errors:"));
5085
+ for (const item of scored) {
5086
+ out.push(` ${ansi.dim("\u2022")} ${item.msg}`);
4705
5087
  }
5088
+ out.push("");
4706
5089
  }
4707
- out.push("");
4708
5090
  return out;
4709
5091
  };
4710
- var formatJestOutputVitest = (raw, opts) => {
4711
- const showStacks = Boolean(env.TEST_CLI_STACKS);
4712
- const cwd = (opts?.cwd ?? process.cwd()).replace(/\\/g, "/");
4713
- const projectHint = new RegExp(
4714
- `(${cwd.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")})|(/gigworx-node/)`
5092
+ var buildFallbackMessageBlock = (messageLines, details) => {
5093
+ const normalize2 = (arr) => arr.map((lineText) => stripAnsiSimple(lineText)).filter((line) => line.trim().length > 0);
5094
+ const normalized = normalize2(messageLines);
5095
+ const informative = normalized.filter((line) => !/^\s*(?:Error|AssertionError):?\s*$/.test(line));
5096
+ if (informative.length > 0) {
5097
+ return [];
5098
+ }
5099
+ const errorIdx = normalized.findIndex(
5100
+ (line) => /(TypeError|ReferenceError|SyntaxError|RangeError|AssertionError|Error):?/.test(line)
4715
5101
  );
4716
- const onlyFailures = Boolean(opts?.onlyFailures);
4717
- const lines = raw.split(/\r?\n/);
5102
+ const collected = [];
5103
+ if (errorIdx >= 0) {
5104
+ for (let i = errorIdx; i < normalized.length && collected.length < 8; i += 1) {
5105
+ const ln = normalized[i];
5106
+ if (!ln.trim()) {
5107
+ break;
5108
+ }
5109
+ if (isStackLine(ln)) {
5110
+ break;
5111
+ }
5112
+ collected.push(ln);
5113
+ }
5114
+ }
5115
+ const fromDetails = collected.length > 0 ? [] : normalize2(details.messages).slice(0, 6);
5116
+ const linesToShow = collected.length > 0 ? collected : fromDetails;
5117
+ if (linesToShow.length === 0) {
5118
+ return [];
5119
+ }
4718
5120
  const out = [];
4719
- const seenFailures = /* @__PURE__ */ new Set();
4720
- const seenFiles = /* @__PURE__ */ new Set();
4721
- for (let lineIndex = 0; lineIndex < lines.length; ) {
4722
- const ln = stripAnsiSimple(lines[lineIndex]);
4723
- if (/^\s*●\s+/.test(ln)) {
4724
- const title = ln.replace(/^\s*●\s+/, "").trim();
4725
- const block = [lines[lineIndex]];
4726
- let scanIndex = lineIndex + 1;
4727
- while (scanIndex < lines.length) {
4728
- const scanLine = stripAnsiSimple(lines[scanIndex]);
4729
- const nextIsStart = /^\s*●\s+/.test(scanLine) || /^\s*(PASS|FAIL)\s/.test(scanLine) || /^\s*Test Suites:/.test(scanLine);
4730
- if (nextIsStart && stripAnsiSimple(lines[scanIndex - 1] ?? "").trim() === "") {
4731
- break;
4732
- }
4733
- block.push(lines[scanIndex]);
4734
- scanIndex += 1;
5121
+ out.push(` ${ansi.bold("Message:")}`);
5122
+ for (const lineText of linesToShow) {
5123
+ out.push(` ${ansi.yellow(lineText)}`);
5124
+ }
5125
+ out.push("");
5126
+ return out;
5127
+ };
5128
+ var buildThrownSection = (details) => {
5129
+ const toLines = (value) => {
5130
+ if (value == null) {
5131
+ return [];
5132
+ }
5133
+ if (typeof value === "string") {
5134
+ return value.split(/\r?\n/);
5135
+ }
5136
+ try {
5137
+ return JSON.stringify(value, null, 2).split(/\r?\n/);
5138
+ } catch {
5139
+ return [String(value)];
5140
+ }
5141
+ };
5142
+ const candidates = [];
5143
+ for (const d of details) {
5144
+ const obj = d && typeof d === "object" ? d : null;
5145
+ if (obj && obj.error && typeof obj.error === "object") {
5146
+ const err = obj.error;
5147
+ if (typeof err.name === "string") {
5148
+ candidates.push(`name: ${err.name}`);
4735
5149
  }
4736
- const codeFrameStart = block.findIndex(
4737
- (candidateLine) => /^\s*(>?\s*\d+\s*\|)/.test(stripAnsiSimple(candidateLine))
4738
- );
4739
- const location = firstTestLocation(block, projectHint);
4740
- const rel = location ? location.split(":")[0].replace(/\\/g, "/").replace(`${cwd}/`, "") : "";
4741
- const key = `${rel}|${title}`;
4742
- if (seenFailures.has(key)) {
4743
- lineIndex = scanIndex;
5150
+ if (typeof err.message === "string") {
5151
+ candidates.push(`message: ${err.message}`);
5152
+ }
5153
+ if (typeof err.code === "string" || typeof err.code === "number") {
5154
+ candidates.push(`code: ${String(err.code)}`);
5155
+ }
5156
+ if (typeof err.cause === "string") {
5157
+ candidates.push(`cause: ${err.cause}`);
5158
+ }
5159
+ if (err.cause && typeof err.cause === "object") {
5160
+ candidates.push("cause:");
5161
+ candidates.push(...toLines(err.cause));
5162
+ }
5163
+ const rest = { ...err };
5164
+ delete rest.name;
5165
+ delete rest.message;
5166
+ delete rest.code;
5167
+ delete rest.stack;
5168
+ if (Object.keys(rest).length > 0) {
5169
+ candidates.push("details:");
5170
+ candidates.push(...toLines(rest));
5171
+ }
5172
+ }
5173
+ }
5174
+ if (!candidates.length) {
5175
+ return [];
5176
+ }
5177
+ const out = [];
5178
+ out.push(` ${ansi.bold("Thrown:")}`);
5179
+ for (const line of candidates.slice(0, 50)) {
5180
+ out.push(` ${ansi.yellow(line)}`);
5181
+ }
5182
+ out.push("");
5183
+ return out;
5184
+ };
5185
+ var mkPrettyFns = () => ({
5186
+ drawRule,
5187
+ drawFailLine,
5188
+ renderRunLine,
5189
+ buildPerFileOverview,
5190
+ buildFileBadgeLine,
5191
+ extractBridgePath,
5192
+ buildCodeFrameSection,
5193
+ buildMessageSection,
5194
+ buildPrettyDiffSection,
5195
+ buildStackSection,
5196
+ deepestProjectLoc,
5197
+ findCodeFrameStart,
5198
+ linesFromDetails,
5199
+ buildFallbackMessageBlock,
5200
+ buildThrownSection
5201
+ });
5202
+
5203
+ // src/lib/formatter/render.ts
5204
+ var relPath = (abs, cwd) => abs.replace(/\\/g, "/").replace(`${cwd}/`, "");
5205
+ var renderChunks = (chunks, ctx, fns, opts) => {
5206
+ const out = [];
5207
+ const seenFiles = /* @__PURE__ */ new Set();
5208
+ const seenFailures = /* @__PURE__ */ new Set();
5209
+ const onlyFailures = Boolean(opts?.onlyFailures);
5210
+ let currentRelFile = null;
5211
+ const escapeRegExp = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5212
+ for (const ch of chunks) {
5213
+ if (ch.tag === "PassFail") {
5214
+ const rel = relPath(ch.rel, ctx.cwd);
5215
+ if (seenFiles.has(rel)) {
4744
5216
  continue;
4745
5217
  }
4746
- seenFailures.add(key);
4747
- out.push(drawFailLine());
4748
- const header = `${colorTokens.fail("\xD7")} ${ansi.white(rel ? `${rel} > ${title}` : title)}`;
4749
- out.push(header);
4750
- const linesBlock = block.map(String);
4751
- const collapsedForSrc = collapseStacks(linesBlock.slice(0));
4752
- if (codeFrameStart >= 0) {
4753
- out.push("");
4754
- out.push(...renderCodeFrame(linesBlock, codeFrameStart));
4755
- out.push("");
4756
- } else if (showStacks) {
4757
- const deepestIdxForSrc = findLastProjectFrameIndex(collapsedForSrc, projectHint);
4758
- const locForSrc = deepestIdxForSrc >= 0 ? stackLocation(collapsedForSrc[deepestIdxForSrc]) : null;
4759
- if (locForSrc) {
4760
- out.push("");
4761
- out.push(...renderSourceCodeFrame(locForSrc.file, locForSrc.line));
4762
- out.push("");
4763
- }
4764
- }
4765
- const payload = extractExpectedReceived(void 0, linesBlock);
4766
- const hasPretty = payload.expected !== void 0 || payload.received !== void 0;
4767
- out.push(...renderPrettyDiff(payload));
4768
- const detailsForMsg = linesFromDetails(void 0);
4769
- const collapsedForTail = collapseStacks(linesBlock.slice(0));
4770
- const stackPreview = showStacks ? firstProjectFrames(collapsedForTail, projectHint, 2) : [];
5218
+ seenFiles.add(rel);
5219
+ currentRelFile = rel;
5220
+ if (!(onlyFailures && ch.badge === "PASS")) {
5221
+ out.push(fns.buildFileBadgeLine(rel, ch.badge === "FAIL" ? 1 : 0));
5222
+ }
5223
+ continue;
5224
+ }
5225
+ if (ch.tag === "FailureBlock") {
5226
+ out.push(fns.drawFailLine());
5227
+ const location = firstTestLocation(ch.lines, ctx.projectHint);
5228
+ const rel = location ? relPath(location.split(":")[0] ?? "", ctx.cwd) : "";
5229
+ const headerText = rel ? `${rel} > ${ch.title}` : ch.title;
5230
+ out.push(`${Colors.Failure("\xD7")} ${ansi.white(headerText)}`);
5231
+ const codeStart = fns.findCodeFrameStart(ch.lines);
5232
+ const collapsedForSrc = collapseStacks(ch.lines.slice(0));
5233
+ const deepestLoc = fns.deepestProjectLoc(collapsedForSrc, ctx.projectHint);
5234
+ let effectiveLoc = deepestLoc;
5235
+ if (!effectiveLoc && currentRelFile) {
5236
+ try {
5237
+ const abs = path9.resolve(ctx.cwd, currentRelFile);
5238
+ const source = ctx.readSource(abs);
5239
+ const testName = (() => {
5240
+ const parts = ch.title.split(">");
5241
+ return (parts[parts.length - 1] || ch.title).trim();
5242
+ })();
5243
+ const itRe = new RegExp(
5244
+ String.raw`\b(?:it|test)\s*\(\s*['\"]${escapeRegExp(testName)}['\"]`
5245
+ );
5246
+ let index = source.findIndex((line) => itRe.test(line));
5247
+ if (index < 0) {
5248
+ index = source.findIndex((line) => /\bexpect\s*\(/.test(line));
5249
+ } else {
5250
+ const windowEnd = Math.min(source.length, index + 80);
5251
+ for (let i = index; i < windowEnd; i += 1) {
5252
+ if (/\bexpect\s*\(/.test(source[i])) {
5253
+ index = i;
5254
+ break;
5255
+ }
5256
+ }
5257
+ }
5258
+ if (index >= 0) {
5259
+ effectiveLoc = { file: abs.replace(/\\/g, "/"), line: index + 1 };
5260
+ }
5261
+ } catch {
5262
+ }
5263
+ }
5264
+ if (codeStart >= 0) {
5265
+ out.push("", ...fns.buildCodeFrameSection(ch.lines, ctx, effectiveLoc), "");
5266
+ } else {
5267
+ out.push("", ...fns.buildCodeFrameSection(ch.lines, ctx, effectiveLoc), "");
5268
+ }
5269
+ const pretty = fns.buildPrettyDiffSection(void 0, ch.lines);
5270
+ out.push(...pretty);
5271
+ const hasPretty = pretty.length > 0;
5272
+ const details = fns.linesFromDetails(void 0);
5273
+ const minimal = (() => {
5274
+ const plain = ch.lines.map((ln) => stripAnsiSimple(ln));
5275
+ const hint = plain.findIndex(
5276
+ (lineText) => /expect\(.+?\)\.(?:to|not\.)/.test(lineText) || /\bError:?\b/.test(lineText)
5277
+ );
5278
+ const acc = [];
5279
+ const start = hint >= 0 ? hint : 0;
5280
+ for (let i = start; i < plain.length; i += 1) {
5281
+ const ln = plain[i];
5282
+ if (!ln.trim()) {
5283
+ break;
5284
+ }
5285
+ if (isStackLine(ln)) {
5286
+ break;
5287
+ }
5288
+ acc.push(ln);
5289
+ }
5290
+ return acc;
5291
+ })();
5292
+ const collapsedForTail = collapseStacks(ch.lines.slice(0));
5293
+ const stackPreview = ctx.showStacks ? collapsedForTail.filter((ln) => isStackLine(stripAnsiSimple(ln))).filter((ln) => ctx.projectHint.test(stripAnsiSimple(ln))).slice(0, 2).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`) : [];
4771
5294
  out.push(
4772
- ...buildMessageSection(
4773
- linesBlock,
4774
- detailsForMsg,
4775
- { projectHint, editorCmd: opts?.editorCmd, showStacks },
4776
- { suppressDiff: hasPretty, stackPreview }
4777
- )
5295
+ ...fns.buildMessageSection(minimal.length ? minimal : ch.lines, details, ctx, {
5296
+ suppressDiff: hasPretty,
5297
+ stackPreview
5298
+ })
4778
5299
  );
4779
- if (showStacks && stackPreview.length === 0) {
4780
- const collapsed = collapseStacks(linesBlock.slice(0));
4781
- out.push(
4782
- ...buildStackSection(collapsed, {
4783
- projectHint,
4784
- editorCmd: opts?.editorCmd,
4785
- showStacks
4786
- })
4787
- );
5300
+ if (minimal.length === 0 && fns.buildFallbackMessageBlock) {
5301
+ out.push(...fns.buildFallbackMessageBlock(ch.lines, { messages: details.messages }));
4788
5302
  }
4789
- out.push(drawFailLine());
4790
- out.push("");
4791
- lineIndex = scanIndex;
4792
- continue;
4793
- }
4794
- const passFail = ln.match(/^\s*(PASS|FAIL)\s+(.+)$/);
4795
- if (passFail) {
4796
- const badge = passFail[1];
4797
- const fileAbs = passFail[2];
4798
- const rel = fileAbs.replace(/\\/g, "/").replace(`${cwd}/`, "");
4799
- if (seenFiles.has(rel)) {
4800
- lineIndex += 1;
4801
- continue;
5303
+ const consoleInline = (() => {
5304
+ const plain = ch.lines.map((ln) => stripAnsiSimple(ln));
5305
+ const cand = plain.filter((ln) => /\bconsole\.(error|warn)\s*\(/i.test(ln) || /^\s*Error:/.test(ln)).map((ln) => ln.trim()).filter((ln) => ln.length > 0).sort((a, b) => b.length - a.length).slice(0, 3);
5306
+ return cand;
5307
+ })();
5308
+ if (consoleInline.length > 0) {
5309
+ out.push(ansi.dim(" Console errors:"), ...consoleInline.map((ln) => ` ${ln}`), "");
4802
5310
  }
4803
- seenFiles.add(rel);
4804
- if (!(onlyFailures && badge === "PASS")) {
4805
- const pill = badge === "PASS" ? colorTokens.passPill("PASS") : colorTokens.failPill("FAIL");
4806
- out.push(`${pill} ${ansi.white(rel)}`);
5311
+ if (ctx.showStacks && fns.buildStackSection && stackPreview.length === 0) {
5312
+ const collapsed = collapseStacks(ch.lines.slice(0));
5313
+ out.push(...fns.buildStackSection(collapsed, ctx));
5314
+ }
5315
+ out.push(fns.drawFailLine(), "");
5316
+ if (rel) {
5317
+ seenFailures.add(`${rel}|${ch.title}`);
4807
5318
  }
4808
- lineIndex += 1;
4809
5319
  continue;
4810
5320
  }
4811
- if (/^\s*(Test Suites:|Tests:|Snapshots:|Time:|Ran all)/.test(ln)) {
4812
- out.push(lines[lineIndex]);
4813
- lineIndex += 1;
5321
+ if (ch.tag === "Summary") {
5322
+ out.push(ch.line);
4814
5323
  continue;
4815
5324
  }
4816
- if (isStackLine(ln)) {
4817
- if (showStacks) {
4818
- const kept = collapseStacks([lines[lineIndex]]);
4819
- out.push(...kept);
5325
+ if (ch.tag === "Stack") {
5326
+ if (ctx.showStacks) {
5327
+ out.push(ch.line);
4820
5328
  }
4821
- lineIndex += 1;
4822
5329
  continue;
4823
5330
  }
4824
- out.push(lines[lineIndex]);
4825
- lineIndex += 1;
5331
+ if (!onlyFailures) {
5332
+ out.push(ch.line);
5333
+ }
4826
5334
  }
4827
- const rendered = out.join("\n");
4828
- const hadParsedTests = seenFiles.size > 0 || seenFailures.size > 0 || out.some((lineText) => /^(?:\s*)(PASS|FAIL)\b/.test(stripAnsiSimple(lineText)));
4829
- if (!hadParsedTests) {
4830
- const bridgePath = extractBridgePath(raw, cwd);
4831
- if (bridgePath && fs4.existsSync(bridgePath)) {
4832
- try {
4833
- const json = JSON.parse(fs4.readFileSync(bridgePath, "utf8"));
4834
- const bridge = coerceJestJsonToBridge(json);
4835
- const renderedFromJson = renderVitestFromJestJSON(bridge, opts);
4836
- const prefix = out.join("\n");
4837
- return prefix ? `${prefix}
4838
- ${renderedFromJson}` : renderedFromJson;
4839
- } catch {
4840
- }
5335
+ const hadParsed = seenFiles.size > 0 || seenFailures.size > 0 || out.some((lineText) => /^(?:\s*)(PASS|FAIL)\b/.test(stripAnsiSimple(lineText)));
5336
+ return { text: out.join("\n"), hadParsed };
5337
+ };
5338
+
5339
+ // src/lib/formatter/context.ts
5340
+ var fs5 = __toESM(require("node:fs"), 1);
5341
+ var makeCtx = (opts, showStacks = false) => {
5342
+ const cwd = (opts?.cwd ?? process.cwd()).replace(/\\/g, "/");
5343
+ const width = Math.max(
5344
+ 40,
5345
+ process.stdout && process.stdout.columns || 80
5346
+ );
5347
+ const projectHint = new RegExp(
5348
+ `(${cwd.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")})|(/gigworx-node/)`
5349
+ );
5350
+ const readSource2 = (file) => {
5351
+ try {
5352
+ return fs5.readFileSync(file.replace(/\\/g, "/"), "utf8").split(/\r?\n/);
5353
+ } catch {
5354
+ return [];
4841
5355
  }
5356
+ };
5357
+ return { cwd, width, showStacks, projectHint, editorCmd: opts?.editorCmd, readSource: readSource2 };
5358
+ };
5359
+
5360
+ // src/lib/formatter/bridge.ts
5361
+ var fs6 = __toESM(require("node:fs"), 1);
5362
+ var path10 = __toESM(require("node:path"), 1);
5363
+ var import_json52 = __toESM(require_lib(), 1);
5364
+ var colorTokens2 = {
5365
+ pass: Colors.Success,
5366
+ fail: Colors.Failure,
5367
+ skip: Colors.Skip,
5368
+ todo: Colors.Todo,
5369
+ passPill: (text) => BackgroundColors.Success(ansi.white(` ${text} `)),
5370
+ failPill: (text) => BackgroundColors.Failure(ansi.white(` ${text} `))
5371
+ };
5372
+ var by = (keySelector) => (left, right) => keySelector(left) - keySelector(right);
5373
+ var isObject = (candidateValue) => !!candidateValue && typeof candidateValue === "object";
5374
+ var asHttpList = (candidateValue) => Array.isArray(candidateValue) ? candidateValue : [];
5375
+ var summarizeUrl = (method, url, route) => {
5376
+ const base = route || url || "";
5377
+ const qs = url && url.includes("?") ? ` ? ${url.split("?")[1]}` : "";
5378
+ return [method || "", base, qs].filter(Boolean).join(" ").trim();
5379
+ };
5380
+ var stripBridgeEventsFromConsole = (maybeConsole) => {
5381
+ if (!Array.isArray(maybeConsole)) {
5382
+ return maybeConsole;
4842
5383
  }
4843
- try {
4844
- const preview = rendered.split("\n").slice(0, 2).join("\n");
4845
- console.info(`formatJestOutputVitest: produced ${out.length} lines; preview:
4846
- ${preview}`);
4847
- } catch {
5384
+ return maybeConsole.filter((entry) => {
5385
+ try {
5386
+ const raw = Array.isArray(entry.message) ? entry.message.map(String).join(" ") : String(entry.message ?? "");
5387
+ return !raw.includes("[JEST-BRIDGE-EVENT]");
5388
+ } catch {
5389
+ return true;
5390
+ }
5391
+ });
5392
+ };
5393
+ var extractBridgePath2 = (raw, cwd) => {
5394
+ const matches = Array.from(
5395
+ raw.matchAll(/Test results written to:\s+([^\n\r]+jest-bridge-[^\s'"]+\.json)/g)
5396
+ );
5397
+ if (!matches.length) {
5398
+ return null;
5399
+ }
5400
+ const jsonPath = (matches[matches.length - 1][1] ?? "").trim().replace(/^["'`]|["'`]$/g, "");
5401
+ return path10.isAbsolute(jsonPath) ? jsonPath : path10.resolve(cwd, jsonPath).replace(/\\/g, "/");
5402
+ };
5403
+ var isTransportError = (msg) => {
5404
+ const lowercaseMessage = (msg ?? "").toLowerCase();
5405
+ return /\bsocket hang up\b|\beconnreset\b|\betimedout\b|\beconnrefused\b|\bwrite epipe\b/.test(
5406
+ lowercaseMessage
5407
+ );
5408
+ };
5409
+ var envNumber = (name, fallback) => {
5410
+ const parsed = Number(process.env[name]);
5411
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
5412
+ };
5413
+ var HEADLAMP_HTTP_WINDOW_MS = () => envNumber("HEADLAMP_HTTP_WINDOW_MS", 3e3);
5414
+ var HEADLAMP_HTTP_STRICT_WINDOW_MS = () => envNumber("HEADLAMP_HTTP_STRICT_WINDOW_MS", 600);
5415
+ var HEADLAMP_HTTP_MIN_SCORE = () => envNumber("HEADLAMP_HTTP_MIN_SCORE", 1200);
5416
+ var HEADLAMP_HTTP_DIFF_LIMIT = () => envNumber("HEADLAMP_HTTP_DIFF_LIMIT", 6);
5417
+ var HEADLAMP_HTTP_SHOW_MISS = () => process.env.HEADLAMP_HTTP_MISS === "1";
5418
+ var eventsNear = (http, ts, testPath, windowMs = HEADLAMP_HTTP_WINDOW_MS()) => {
5419
+ if (typeof ts !== "number" || !Number.isFinite(ts)) {
5420
+ return [];
5421
+ }
5422
+ return http.filter((e) => {
5423
+ const timeOk = typeof e.timestampMs === "number" && Math.abs(e.timestampMs - ts) <= windowMs;
5424
+ const pathOk = !testPath || e.testPath === testPath;
5425
+ return timeOk && pathOk;
5426
+ });
5427
+ };
5428
+ var parseMethodPathFromTitle = (title) => {
5429
+ if (!title) {
5430
+ return {};
5431
+ }
5432
+ const matchResult = title.match(/\b(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\s+([^\s)]+)/i);
5433
+ return matchResult ? { method: matchResult[1]?.toUpperCase(), path: matchResult[2] } : {};
5434
+ };
5435
+ var isHttpStatusNumber = (statusNumber) => typeof statusNumber === "number" && statusNumber >= 100 && statusNumber <= 599;
5436
+ var hasStatusSemantics = (assertionLike) => {
5437
+ if (!assertionLike) {
5438
+ return false;
4848
5439
  }
4849
- return rendered;
5440
+ if (isHttpStatusNumber(assertionLike.expectedNumber) || isHttpStatusNumber(assertionLike.receivedNumber)) {
5441
+ return true;
5442
+ }
5443
+ const combinedRaw = `${assertionLike.matcher ?? ""} ${assertionLike.message ?? ""}`;
5444
+ const combinedMessage = combinedRaw.toLowerCase();
5445
+ return /\bstatus(code)?\b|\btohaves(tatus|tatuscode)\b/.test(combinedMessage);
5446
+ };
5447
+ var fileSuggestsHttp = (relPath2) => /(?:^|\/)(routes?|api|controllers?|e2e|integration)(?:\/|\.test\.)/i.test(relPath2);
5448
+ var inferHttpNumbersFromText = (lines) => {
5449
+ const text = lines.join("\n");
5450
+ const match = text.match(/Expected:\s*(\d{3})[\s\S]*?Received:\s*(\d{3})/i);
5451
+ if (match) {
5452
+ return { expectedNumber: Number(match[1]), receivedNumber: Number(match[2]) };
5453
+ }
5454
+ return {};
5455
+ };
5456
+ var titleSuggestsHttp = (title) => {
5457
+ const { method, path: parsedPath } = parseMethodPathFromTitle(title);
5458
+ return Boolean(method || parsedPath && parsedPath.startsWith("/"));
5459
+ };
5460
+ var isHttpRelevant = (ctx) => {
5461
+ const assertionCtx = ctx.assertion;
5462
+ return ctx.hasTransportSignal || ctx.httpCountInSameTest > 0 || titleSuggestsHttp(ctx.title) || hasStatusSemantics(assertionCtx) || fileSuggestsHttp(ctx.relPath);
5463
+ };
5464
+ var routeSimilarityScore = (hint, evt) => {
5465
+ if (!hint.path && !hint.method) {
5466
+ return 0;
5467
+ }
5468
+ const methodOk = hint.method && evt.method ? Number(hint.method === evt.method) : 0;
5469
+ const route = evt.route || evt.url || "";
5470
+ if (!route) {
5471
+ return methodOk * 10;
5472
+ }
5473
+ if (hint.path && route === hint.path) {
5474
+ return 500 + methodOk * 50;
5475
+ }
5476
+ if (hint.path && route.endsWith(hint.path)) {
5477
+ return 300 + methodOk * 50;
5478
+ }
5479
+ if (hint.path && route.includes(hint.path)) {
5480
+ return 200 + methodOk * 50;
5481
+ }
5482
+ return methodOk * 10;
4850
5483
  };
4851
- var isBridgeJSONLike = (candidate) => {
4852
- const candidateValue = candidate;
4853
- return typeof candidateValue === "object" && candidateValue !== null && "aggregated" in candidateValue;
5484
+ var scoreHttpForAssertion = (assertion, titleHint) => (candidateEvent) => {
5485
+ const tsA = assertion.timestampMs;
5486
+ const tsH = candidateEvent.timestampMs;
5487
+ const window = isTransportError(assertion.message) ? HEADLAMP_HTTP_STRICT_WINDOW_MS() : HEADLAMP_HTTP_WINDOW_MS();
5488
+ const timeScore = typeof tsA === "number" && typeof tsH === "number" ? Math.max(0, window - Math.abs(tsA - tsH)) : 0;
5489
+ const statusScore = typeof assertion.receivedNumber === "number" && candidateEvent.statusCode === assertion.receivedNumber ? 1500 : typeof assertion.expectedNumber === "number" && candidateEvent.statusCode === assertion.expectedNumber ? 1200 : (candidateEvent.statusCode ?? 0) >= 400 ? 800 : 0;
5490
+ const routeScore = routeSimilarityScore(titleHint, candidateEvent);
5491
+ const specificity = candidateEvent.route ? 80 : candidateEvent.url ? 40 : 0;
5492
+ return timeScore + statusScore + routeScore + specificity;
5493
+ };
5494
+ var pickRelevantHttp = (assertion, http, ctx) => {
5495
+ const hint = parseMethodPathFromTitle(ctx.title);
5496
+ const nameMatches = (leftName, rightName) => !!leftName && !!rightName && (leftName === rightName || leftName.includes(rightName) || rightName.includes(leftName));
5497
+ const sameTest = (leftCtx, rightCtx) => !!leftCtx && !!rightCtx && leftCtx.testPath === rightCtx.testPath && nameMatches(leftCtx.currentTestName, rightCtx.currentTestName);
5498
+ const strictPool = http.filter(
5499
+ (httpEvent) => sameTest(assertion, httpEvent) || sameTest(ctx, httpEvent)
5500
+ );
5501
+ const windowMs = isTransportError(assertion.message) ? HEADLAMP_HTTP_STRICT_WINDOW_MS() : HEADLAMP_HTTP_WINDOW_MS();
5502
+ let pool = strictPool;
5503
+ if (!pool.length) {
5504
+ pool = http.filter(
5505
+ (e) => e.testPath === ctx.testPath && typeof assertion.timestampMs === "number" && typeof e.timestampMs === "number" && Math.abs(e.timestampMs - assertion.timestampMs) <= windowMs
5506
+ );
5507
+ }
5508
+ if (!pool.length) {
5509
+ pool = http.filter(
5510
+ (e) => typeof assertion.timestampMs === "number" && typeof e.timestampMs === "number" && Math.abs(e.timestampMs - assertion.timestampMs) <= windowMs
5511
+ );
5512
+ }
5513
+ if (!pool.length) {
5514
+ return void 0;
5515
+ }
5516
+ const scored = pool.map((httpEvent) => ({ h: httpEvent, s: scoreHttpForAssertion(assertion, hint)(httpEvent) })).sort((leftScore, rightScore) => rightScore.s - leftScore.s);
5517
+ const [best] = scored;
5518
+ const threshold = isTransportError(assertion.message) ? Math.max(HEADLAMP_HTTP_MIN_SCORE(), 1400) : HEADLAMP_HTTP_MIN_SCORE();
5519
+ return best && best.s >= threshold ? best.h : void 0;
4854
5520
  };
4855
- function coerceJestJsonToBridge(raw) {
5521
+ var isBridgeJSONLike = (candidateValue) => !!candidateValue && typeof candidateValue === "object" && "aggregated" in candidateValue;
5522
+ var coerceJestJsonToBridge = (raw) => {
4856
5523
  if (isBridgeJSONLike(raw)) {
4857
5524
  return raw;
4858
5525
  }
@@ -4862,18 +5529,21 @@ function coerceJestJsonToBridge(raw) {
4862
5529
  }
4863
5530
  return {
4864
5531
  startTime: Number(j.startTime ?? Date.now()),
4865
- testResults: j.testResults.map((testFileResult) => ({
4866
- testFilePath: testFileResult.testFilePath || testFileResult.name || "",
4867
- status: testFileResult.status,
4868
- failureMessage: testFileResult.failureMessage || "",
4869
- failureDetails: testFileResult.failureDetails ?? [],
4870
- testResults: (testFileResult.assertionResults || []).map((assertion) => ({
5532
+ testResults: j.testResults.map((tr) => ({
5533
+ testFilePath: tr.testFilePath || tr.name || "",
5534
+ status: tr.status,
5535
+ failureMessage: tr.failureMessage || "",
5536
+ failureDetails: tr.failureDetails ?? [],
5537
+ testExecError: tr.testExecError ?? null,
5538
+ console: tr.console ?? null,
5539
+ testResults: (tr.assertionResults || []).map((assertion) => ({
4871
5540
  title: assertion.title,
4872
5541
  fullName: assertion.fullName || [...assertion.ancestorTitles || [], assertion.title].join(" "),
4873
5542
  status: assertion.status,
4874
5543
  duration: assertion.duration || 0,
4875
5544
  location: assertion.location ?? null,
4876
- failureMessages: assertion.failureMessages || []
5545
+ failureMessages: assertion.failureMessages || [],
5546
+ failureDetails: assertion.failureDetails || []
4877
5547
  }))
4878
5548
  })),
4879
5549
  aggregated: {
@@ -4889,21 +5559,20 @@ function coerceJestJsonToBridge(raw) {
4889
5559
  success: j.success
4890
5560
  }
4891
5561
  };
4892
- }
4893
- var vitestFooter = (agg, _startedAt, durationMs) => {
5562
+ };
5563
+ var vitestFooter = (agg, durationMs) => {
4894
5564
  const files = [
4895
- agg.numFailedTestSuites ? colorTokens.fail(`${agg.numFailedTestSuites} failed`) : "",
4896
- agg.numPassedTestSuites ? colorTokens.pass(`${agg.numPassedTestSuites} passed`) : "",
4897
- agg.numPendingTests ? colorTokens.skip(`${agg.numPendingTests} skipped`) : ""
5565
+ agg.numFailedTestSuites ? colorTokens2.fail(`${agg.numFailedTestSuites} failed`) : "",
5566
+ agg.numPassedTestSuites ? colorTokens2.pass(`${agg.numPassedTestSuites} passed`) : "",
5567
+ agg.numPendingTests ? colorTokens2.skip(`${agg.numPendingTests} skipped`) : ""
4898
5568
  ].filter(Boolean).join(ansi.dim(" | "));
4899
5569
  const tests = [
4900
- agg.numFailedTests ? colorTokens.fail(`${agg.numFailedTests} failed`) : "",
4901
- agg.numPassedTests ? colorTokens.pass(`${agg.numPassedTests} passed`) : "",
4902
- agg.numPendingTests ? colorTokens.skip(`${agg.numPendingTests} skipped`) : "",
4903
- agg.numTodoTests ? colorTokens.todo(`${agg.numTodoTests} todo`) : ""
5570
+ agg.numFailedTests ? colorTokens2.fail(`${agg.numFailedTests} failed`) : "",
5571
+ agg.numPassedTests ? colorTokens2.pass(`${agg.numPassedTests} passed`) : "",
5572
+ agg.numPendingTests ? colorTokens2.skip(`${agg.numPendingTests} skipped`) : "",
5573
+ agg.numTodoTests ? colorTokens2.todo(`${agg.numTodoTests} todo`) : ""
4904
5574
  ].filter(Boolean).join(ansi.dim(" | "));
4905
- const durMs = typeof durationMs === "number" ? durationMs : typeof agg.runTimeMs === "number" ? agg.runTimeMs : void 0;
4906
- const time = durMs != null ? `${Math.max(0, Math.round(durMs))}ms` : "";
5575
+ const time = durationMs != null ? `${Math.max(0, Math.round(durationMs))}ms` : typeof agg.runTimeMs === "number" ? `${Math.max(0, Math.round(agg.runTimeMs))}ms` : "";
4907
5576
  const thread = ansi.dim("(in thread 0ms, 0.00%)");
4908
5577
  return [
4909
5578
  `${ansi.bold("Test Files")} ${files} ${ansi.dim(`(${agg.numTotalTestSuites})`)}`,
@@ -4911,20 +5580,14 @@ var vitestFooter = (agg, _startedAt, durationMs) => {
4911
5580
  `${ansi.bold("Time")} ${time} ${thread}`
4912
5581
  ].join("\n");
4913
5582
  };
4914
- function renderVitestFromJestJSON(data, opts) {
4915
- const cwd = (opts?.cwd ?? process.cwd()).replace(/\\/g, "/");
4916
- const projectHint = new RegExp(
4917
- `(${cwd.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")})|(/gigworx-node/)`
4918
- );
4919
- const ctx = { projectHint, editorCmd: opts?.editorCmd, showStacks: true };
4920
- const onlyFailures = Boolean(opts?.onlyFailures);
5583
+ var renderVitestFromJestJSON = (data, ctx, opts) => {
4921
5584
  const out = [];
5585
+ const onlyFailures = Boolean(opts?.onlyFailures);
4922
5586
  if (!onlyFailures) {
4923
- out.push(renderRunLine(cwd));
4924
- out.push("");
5587
+ out.push(`${BackgroundColors.Run(ansi.white(" RUN "))} ${ansi.dim(ctx.cwd)}`, "");
4925
5588
  }
4926
5589
  for (const file of data.testResults) {
4927
- const rel = file.testFilePath.replace(/\\/g, "/").replace(`${cwd}/`, "");
5590
+ const rel = file.testFilePath.replace(/\\/g, "/").replace(`${ctx.cwd}/`, "");
4928
5591
  const failed = file.testResults.filter((assertion) => assertion.status === "failed");
4929
5592
  if (!onlyFailures) {
4930
5593
  out.push(...buildPerFileOverview(rel, file.testResults));
@@ -4932,79 +5595,490 @@ function renderVitestFromJestJSON(data, opts) {
4932
5595
  if (!(onlyFailures && failed.length === 0)) {
4933
5596
  out.push(buildFileBadgeLine(rel, failed.length));
4934
5597
  }
4935
- if (file.failureMessage && failed.length === 0) {
5598
+ let httpSorted = [];
5599
+ let assertionEvents = [];
5600
+ {
5601
+ const parseBridge = (consoleEntries) => {
5602
+ const http = [];
5603
+ const assertions = [];
5604
+ if (!Array.isArray(consoleEntries)) {
5605
+ return { http, assertions };
5606
+ }
5607
+ for (const entry of consoleEntries) {
5608
+ const rec = entry;
5609
+ const rawMsgVal = rec && typeof rec.message !== "undefined" ? rec.message : "";
5610
+ const raw = Array.isArray(rawMsgVal) ? rawMsgVal.map(String).join(" ") : String(rawMsgVal ?? "");
5611
+ if (!raw.includes("[JEST-BRIDGE-EVENT]")) {
5612
+ continue;
5613
+ }
5614
+ const jsonText = raw.split("[JEST-BRIDGE-EVENT]").pop()?.trim() ?? "";
5615
+ try {
5616
+ const evt = import_json52.default.parse(jsonText);
5617
+ const type = evt?.type;
5618
+ if (type === "httpResponse") {
5619
+ const timestampMs = Number(evt.timestampMs ?? Date.now());
5620
+ http.push({
5621
+ kind: "response",
5622
+ timestampMs,
5623
+ method: evt.method,
5624
+ url: evt.url,
5625
+ route: evt.route,
5626
+ statusCode: evt.statusCode,
5627
+ durationMs: evt.durationMs,
5628
+ contentType: evt.contentType,
5629
+ requestId: evt.requestId,
5630
+ json: evt.json,
5631
+ bodyPreview: evt.bodyPreview,
5632
+ testPath: evt.testPath,
5633
+ currentTestName: evt.currentTestName
5634
+ });
5635
+ } else if (type === "httpAbort") {
5636
+ http.push({
5637
+ kind: "abort",
5638
+ timestampMs: Number(evt.timestampMs ?? Date.now()),
5639
+ method: evt.method,
5640
+ url: evt.url,
5641
+ route: evt.route,
5642
+ durationMs: evt.durationMs,
5643
+ testPath: evt.testPath,
5644
+ currentTestName: evt.currentTestName
5645
+ });
5646
+ } else if (type === "httpResponseBatch") {
5647
+ const list = asHttpList(evt?.events);
5648
+ for (const item of list) {
5649
+ const anyItem = item;
5650
+ http.push({
5651
+ timestampMs: Number(anyItem.timestampMs ?? Date.now()),
5652
+ method: anyItem.method,
5653
+ url: anyItem.url,
5654
+ route: anyItem.route,
5655
+ statusCode: anyItem.statusCode,
5656
+ durationMs: anyItem.durationMs,
5657
+ contentType: anyItem.contentType,
5658
+ requestId: anyItem.requestId,
5659
+ json: anyItem.json,
5660
+ bodyPreview: anyItem.bodyPreview,
5661
+ testPath: evt.testPath,
5662
+ currentTestName: evt.currentTestName
5663
+ });
5664
+ }
5665
+ } else if (type === "assertionFailure") {
5666
+ assertions.push({
5667
+ timestampMs: typeof evt.timestampMs === "number" ? evt.timestampMs : void 0,
5668
+ matcher: evt.matcher,
5669
+ expectedNumber: typeof evt.expectedNumber === "number" ? evt.expectedNumber : void 0,
5670
+ receivedNumber: typeof evt.receivedNumber === "number" ? evt.receivedNumber : void 0,
5671
+ message: typeof evt.message === "string" ? evt.message : void 0,
5672
+ stack: typeof evt.stack === "string" ? evt.stack : void 0,
5673
+ testPath: evt.testPath,
5674
+ currentTestName: evt.currentTestName,
5675
+ expectedPreview: typeof evt.expectedPreview === "string" ? evt.expectedPreview : void 0,
5676
+ actualPreview: typeof evt.actualPreview === "string" ? evt.actualPreview : void 0
5677
+ });
5678
+ }
5679
+ } catch {
5680
+ }
5681
+ }
5682
+ return { http, assertions };
5683
+ };
5684
+ const parsed = parseBridge(file.console);
5685
+ httpSorted = [...parsed.http].sort(by((event) => event.timestampMs));
5686
+ assertionEvents = parsed.assertions;
5687
+ }
5688
+ const inSameCtx = (testPath, testName) => httpSorted.filter(
5689
+ (event) => event.testPath === testPath && event.currentTestName === testName
5690
+ );
5691
+ if (file.failureMessage || file.testExecError) {
4936
5692
  const lines = file.failureMessage.split(/\r?\n/);
4937
- const details = linesFromDetails(file.failureDetails);
4938
- const mergedForStack = collapseStacks([...lines, ...details.stacks]);
4939
- const synthLoc = deepestProjectLoc(mergedForStack, projectHint);
5693
+ const combinedDetails = (() => {
5694
+ const base = linesFromDetails(file.failureDetails);
5695
+ const exec = linesFromDetails(
5696
+ Array.isArray(file.testExecError) ? file.testExecError : [file.testExecError]
5697
+ );
5698
+ return {
5699
+ stacks: [...base.stacks, ...exec.stacks],
5700
+ messages: [...base.messages, ...exec.messages]
5701
+ };
5702
+ })();
5703
+ const mergedForStack = collapseStacks([...lines, ...combinedDetails.stacks]);
5704
+ const synthLoc = deepestProjectLoc(mergedForStack, ctx.projectHint);
4940
5705
  out.push(...buildCodeFrameSection(lines, ctx, synthLoc));
4941
- const payload = extractExpectedReceived(file.failureDetails, lines);
4942
- const hasPretty = payload.expected !== void 0 || payload.received !== void 0;
4943
- out.push(...renderPrettyDiff(payload));
4944
- const stackPreview = ctx.showStacks ? firstProjectFrames(mergedForStack, projectHint, 2) : [];
5706
+ const payloadPretty = buildPrettyDiffSection(file.failureDetails, lines);
5707
+ out.push(...payloadPretty);
5708
+ const hasPretty = payloadPretty.length > 0;
5709
+ const stackPreview = ctx.showStacks ? mergedForStack.filter((ln) => isStackLine(stripAnsiSimple(ln))).filter((ln) => ctx.projectHint.test(stripAnsiSimple(ln))).slice(0, 2).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`) : [];
4945
5710
  out.push(
4946
- ...buildMessageSection(lines, details, ctx, {
5711
+ ...buildMessageSection(lines, combinedDetails, ctx, {
4947
5712
  suppressDiff: hasPretty,
4948
5713
  stackPreview
4949
5714
  })
4950
5715
  );
4951
- out.push(...buildConsoleSection(file.console ?? null));
5716
+ out.push(...buildConsoleSection(stripBridgeEventsFromConsole(file.console ?? null)));
4952
5717
  if (ctx.showStacks && stackPreview.length === 0) {
4953
- out.push(...buildStackSection(mergedForStack, ctx));
5718
+ const tail = mergedForStack.filter((ln) => isStackLine(stripAnsiSimple(ln))).slice(-4).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`);
5719
+ if (tail.length) {
5720
+ out.push(ansi.dim(" Stack:"), ...tail, "");
5721
+ }
4954
5722
  }
4955
5723
  }
4956
- for (const failedAssertion of failed) {
5724
+ for (const assertion of failed) {
4957
5725
  out.push(drawFailLine());
4958
- const header = `${rel} > ${failedAssertion.fullName}`;
4959
- const messagesArray = failedAssertion.failureMessages.length > 0 ? failedAssertion.failureMessages : [""];
4960
- const details = linesFromDetails(file.failureDetails);
5726
+ const header = `${rel} > ${assertion.fullName}`;
5727
+ const messagesArray = (() => {
5728
+ if (assertion.failureMessages && assertion.failureMessages.length > 0) {
5729
+ return assertion.failureMessages;
5730
+ }
5731
+ if (file.failureMessage && file.failureMessage.trim().length > 0) {
5732
+ return file.failureMessage.split(/\r?\n/);
5733
+ }
5734
+ const linesFromMatcher = linesFromDetails(
5735
+ assertion.failureDetails || file.failureDetails
5736
+ ).messages;
5737
+ if (Array.isArray(linesFromMatcher) && linesFromMatcher.length > 0) {
5738
+ return linesFromMatcher;
5739
+ }
5740
+ return [""];
5741
+ })();
5742
+ const details = linesFromDetails(assertion.failureDetails || file.failureDetails);
5743
+ const matcherMsg = (() => {
5744
+ try {
5745
+ const arr = assertion.failureDetails || file.failureDetails;
5746
+ if (!arr) {
5747
+ return [];
5748
+ }
5749
+ for (const detailEntry of arr) {
5750
+ const obj = detailEntry && typeof detailEntry === "object" ? detailEntry : null;
5751
+ const mr = obj && obj.matcherResult && typeof obj.matcherResult === "object" ? obj.matcherResult : null;
5752
+ if (mr && typeof mr.message === "string" && mr.message.trim()) {
5753
+ const name = typeof mr.matcherName === "string" ? mr.matcherName : "";
5754
+ const matcherHeader = name ? ` ${ansi.bold("Matcher:")} ${ansi.yellow(name)}` : "";
5755
+ const bodyHeader = ` ${ansi.bold("Message:")}`;
5756
+ const body = String(mr.message).split(/\r?\n/).slice(0, 6).map((ln) => ` ${ansi.yellow(ln)}`);
5757
+ return [matcherHeader, bodyHeader, ...body, ""].filter(Boolean);
5758
+ }
5759
+ }
5760
+ } catch {
5761
+ }
5762
+ return [];
5763
+ })();
4961
5764
  const mergedForStack = collapseStacks([...messagesArray, ...details.stacks]);
4962
- const deepestLoc = deepestProjectLoc(mergedForStack, projectHint);
4963
- const locLink = deepestLoc && (() => {
4964
- const href = preferredEditorHref(deepestLoc.file, deepestLoc.line, opts?.editorCmd);
4965
- const base = `${path9.basename(deepestLoc.file)}:${deepestLoc.line}`;
5765
+ const deepestLoc = deepestProjectLoc(mergedForStack, ctx.projectHint);
5766
+ const locLink = deepestLoc ? (() => {
5767
+ const href = preferredEditorHref(deepestLoc.file, deepestLoc.line, ctx.editorCmd);
5768
+ const base = `${deepestLoc.file.split("/").pop()}:${deepestLoc.line}`;
4966
5769
  return osc8(base, href);
4967
- })();
5770
+ })() : void 0;
5771
+ const bullet = (text) => `${Colors.Failure("\xD7")} ${ansi.white(text)}`;
4968
5772
  const headerLine = `${ansi.white(header)}${locLink ? ` ${ansi.dim(`(${locLink})`)}` : ""}`;
4969
- const bullet = (text) => `${colorTokens.fail("\xD7")} ${ansi.white(text)}`;
4970
5773
  out.push(bullet(headerLine));
4971
5774
  const msgLines = messagesArray.join("\n").split("\n");
4972
- const assertFallback = deepestLoc || failedAssertion.location && {
4973
- file: file.testFilePath,
4974
- line: failedAssertion.location.line
4975
- };
4976
- out.push("", ...buildCodeFrameSection(msgLines, ctx, assertFallback), "");
4977
- const payload = extractExpectedReceived(file.failureDetails, msgLines);
4978
- const hasPretty = payload.expected !== void 0 || payload.received !== void 0;
4979
- out.push(...renderPrettyDiff(payload));
4980
- const stackPreview = ctx.showStacks ? firstProjectFrames(mergedForStack, projectHint, 2) : [];
5775
+ const assertFallback = deepestLoc || assertion.location && { file: file.testFilePath, line: assertion.location.line };
5776
+ out.push("", ...buildCodeFrameSection(msgLines, ctx, assertFallback || void 0), "");
5777
+ const pretty = buildPrettyDiffSection(
5778
+ assertion.failureDetails || file.failureDetails,
5779
+ msgLines
5780
+ );
5781
+ out.push(...pretty);
5782
+ const hasPretty = pretty.length > 0;
5783
+ const stackPreview = ctx.showStacks ? mergedForStack.filter((ln) => isStackLine(stripAnsiSimple(ln))).filter((ln) => ctx.projectHint.test(stripAnsiSimple(ln))).slice(0, 2).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`) : [];
5784
+ if (matcherMsg.length) {
5785
+ out.push(...matcherMsg);
5786
+ }
4981
5787
  out.push(
4982
5788
  ...buildMessageSection(msgLines, details, ctx, { suppressDiff: hasPretty, stackPreview })
4983
5789
  );
4984
- if (ctx.showStacks && stackPreview.length === 0) {
4985
- out.push(
4986
- ...buildStackSection(
4987
- mergedForStack,
4988
- ctx,
4989
- failedAssertion.location ? { file: file.testFilePath, line: failedAssertion.location.line } : null
4990
- )
5790
+ {
5791
+ const HEADLAMP_HTTP_DIFF_LIMIT_LOCAL = () => HEADLAMP_HTTP_DIFF_LIMIT();
5792
+ const safeParseJSON = (text) => {
5793
+ try {
5794
+ return text ? import_json52.default.parse(text) : void 0;
5795
+ } catch {
5796
+ return void 0;
5797
+ }
5798
+ };
5799
+ const jsonDiff = (expected, actual, limit = HEADLAMP_HTTP_DIFF_LIMIT_LOCAL()) => {
5800
+ const outChanges = [];
5801
+ const queue = [];
5802
+ queue.push({
5803
+ pathSoFar: "$",
5804
+ expectedValue: expected,
5805
+ actualValue: actual
5806
+ });
5807
+ while (queue.length && outChanges.length < limit) {
5808
+ const { pathSoFar, expectedValue, actualValue } = queue.shift();
5809
+ const expectedIsObj = expectedValue && typeof expectedValue === "object";
5810
+ const actualIsObj = actualValue && typeof actualValue === "object";
5811
+ if (!expectedIsObj && !actualIsObj) {
5812
+ if (JSON.stringify(expectedValue) !== JSON.stringify(actualValue)) {
5813
+ outChanges.push({
5814
+ kind: "changed",
5815
+ path: pathSoFar,
5816
+ preview: `${String(expectedValue)} \u2192 ${String(actualValue)}`
5817
+ });
5818
+ }
5819
+ } else if (expectedIsObj && !actualIsObj) {
5820
+ outChanges.push({
5821
+ kind: "changed",
5822
+ path: pathSoFar,
5823
+ preview: "[object] \u2192 primitive"
5824
+ });
5825
+ } else if (!expectedIsObj && actualIsObj) {
5826
+ outChanges.push({
5827
+ kind: "changed",
5828
+ path: pathSoFar,
5829
+ preview: "primitive \u2192 [object]"
5830
+ });
5831
+ } else {
5832
+ const expectedKeys = new Set(Object.keys(expectedValue));
5833
+ const actualKeys = new Set(Object.keys(actualValue));
5834
+ for (const key of expectedKeys) {
5835
+ if (!actualKeys.has(key) && outChanges.length < limit) {
5836
+ outChanges.push({ kind: "removed", path: `${pathSoFar}.${key}` });
5837
+ }
5838
+ }
5839
+ for (const key of actualKeys) {
5840
+ if (!expectedKeys.has(key) && outChanges.length < limit) {
5841
+ outChanges.push({ kind: "added", path: `${pathSoFar}.${key}` });
5842
+ }
5843
+ }
5844
+ for (const key of expectedKeys) {
5845
+ if (actualKeys.has(key) && outChanges.length < limit) {
5846
+ queue.push({
5847
+ pathSoFar: `${pathSoFar}.${key}`,
5848
+ expectedValue: expectedValue[key],
5849
+ actualValue: actualValue[key]
5850
+ });
5851
+ }
5852
+ }
5853
+ }
5854
+ }
5855
+ return outChanges;
5856
+ };
5857
+ const importantMessages = (json) => {
5858
+ const msgs = [];
5859
+ try {
5860
+ const obj = isObject(json) ? json : {};
5861
+ const pushMaybe = (candidate) => {
5862
+ if (typeof candidate === "string" && candidate.trim()) {
5863
+ msgs.push(candidate);
5864
+ }
5865
+ };
5866
+ pushMaybe(obj.displayMessage);
5867
+ pushMaybe(obj.message);
5868
+ if (Array.isArray(obj.errors)) {
5869
+ for (const element of obj.errors) {
5870
+ pushMaybe(isObject(element) ? element.message : void 0);
5871
+ }
5872
+ }
5873
+ if (Array.isArray(obj.data)) {
5874
+ for (const element of obj.data) {
5875
+ pushMaybe(isObject(element) ? element.message : void 0);
5876
+ }
5877
+ }
5878
+ } catch {
5879
+ }
5880
+ return msgs.slice(0, 2);
5881
+ };
5882
+ const nameMatches = (leftName, rightName) => !!leftName && !!rightName && (leftName === rightName || leftName.includes(rightName) || rightName.includes(leftName));
5883
+ const corresponding = assertionEvents.find(
5884
+ (aevt) => aevt.testPath === file.testFilePath && nameMatches(aevt.currentTestName, assertion.title)
5885
+ ) ?? assertion;
5886
+ const perTestSlice = inSameCtx(file.testFilePath, assertion.title);
5887
+ const nearByTime = eventsNear(
5888
+ httpSorted,
5889
+ corresponding?.timestampMs,
5890
+ file.testFilePath
4991
5891
  );
5892
+ const hasAbort = perTestSlice.some((event) => event.kind === "abort");
5893
+ const hasTransport = isTransportError(corresponding?.message) || hasAbort;
5894
+ const httpLikely = isHttpRelevant({
5895
+ assertion: corresponding,
5896
+ title: assertion.fullName,
5897
+ relPath: rel,
5898
+ httpCountInSameTest: perTestSlice.length || nearByTime.length,
5899
+ hasTransportSignal: hasTransport
5900
+ });
5901
+ if (!httpLikely) {
5902
+ } else {
5903
+ const expPreview = corresponding?.expectedPreview;
5904
+ const actPreview = corresponding?.actualPreview;
5905
+ const parsedExpected = safeParseJSON(expPreview);
5906
+ const parsedActual = safeParseJSON(actPreview);
5907
+ let corr = corresponding;
5908
+ if (!isHttpStatusNumber(corr.expectedNumber) && !isHttpStatusNumber(corr.receivedNumber)) {
5909
+ const inferred = inferHttpNumbersFromText(msgLines);
5910
+ if (isHttpStatusNumber(inferred.expectedNumber) || isHttpStatusNumber(inferred.receivedNumber)) {
5911
+ corr = { ...corr, ...inferred };
5912
+ }
5913
+ }
5914
+ const relevant = pickRelevantHttp(
5915
+ {
5916
+ timestampMs: corr?.timestampMs,
5917
+ expectedNumber: corr?.expectedNumber,
5918
+ receivedNumber: corr?.receivedNumber,
5919
+ matcher: corr?.matcher,
5920
+ message: corr?.message,
5921
+ stack: corr?.stack,
5922
+ testPath: file.testFilePath,
5923
+ currentTestName: assertion.title
5924
+ },
5925
+ httpSorted,
5926
+ {
5927
+ testPath: file.testFilePath,
5928
+ currentTestName: assertion.title,
5929
+ title: assertion.fullName
5930
+ }
5931
+ );
5932
+ if (hasTransport) {
5933
+ const tsBase = corresponding?.timestampMs ?? 0;
5934
+ const abortCandidates = perTestSlice.filter((event) => event.kind === "abort").sort((leftEvent, rightEvent) => {
5935
+ const deltaLeft = Math.abs(tsBase - (leftEvent.timestampMs ?? 0));
5936
+ const deltaRight = Math.abs(tsBase - (rightEvent.timestampMs ?? 0));
5937
+ return deltaLeft - deltaRight;
5938
+ });
5939
+ const [nearestAbort] = abortCandidates;
5940
+ if (nearestAbort) {
5941
+ out.push(
5942
+ " HTTP:",
5943
+ `
5944
+ ${summarizeUrl(nearestAbort.method, nearestAbort.url, nearestAbort.route)} ${ansi.dim("->")} ${ansi.yellow("connection aborted")}`,
5945
+ (() => {
5946
+ const ms = nearestAbort.durationMs;
5947
+ return ms != null ? ` ${ansi.dim(`(${ms}ms)`)} ` : "";
5948
+ })(),
5949
+ "\n"
5950
+ );
5951
+ } else if (relevant) {
5952
+ } else if (HEADLAMP_HTTP_SHOW_MISS()) {
5953
+ out.push(
5954
+ " HTTP:",
5955
+ `
5956
+ ${ansi.dim("Transport error; no matching HTTP exchange in window.")}`,
5957
+ "\n"
5958
+ );
5959
+ }
5960
+ }
5961
+ if (!hasTransport && relevant) {
5962
+ const parts = [];
5963
+ const where = summarizeUrl(relevant.method, relevant.url, relevant.route);
5964
+ const line1 = [
5965
+ " HTTP:",
5966
+ `
5967
+ ${where} ${ansi.dim("->")} ${relevant.statusCode ?? "?"}`,
5968
+ (() => {
5969
+ const ms = relevant.durationMs;
5970
+ return typeof ms === "number" ? ` ${ansi.dim(`(${ms}ms)`)} ` : " ";
5971
+ })(),
5972
+ relevant.contentType ? ansi.dim(`(${relevant.contentType})`) : "",
5973
+ relevant.requestId ? ansi.dim(` reqId=${relevant.requestId}`) : ""
5974
+ ].join("");
5975
+ const expVsAct = (() => {
5976
+ if (typeof corresponding?.expectedNumber === "number" || typeof corresponding?.receivedNumber === "number") {
5977
+ const exp = corresponding?.expectedNumber != null ? String(corresponding.expectedNumber) : "?";
5978
+ const got = corresponding?.receivedNumber != null ? String(corresponding.receivedNumber) : String(relevant.statusCode ?? "?");
5979
+ return `
5980
+ Expected: ${ansi.yellow(exp)} Received: ${ansi.yellow(got)}`;
5981
+ }
5982
+ return "";
5983
+ })();
5984
+ const whyLines = importantMessages(relevant.json).map((msg) => `
5985
+ Why: ${ansi.white(msg)}`).slice(0, 1).join("");
5986
+ const diffLines = (() => {
5987
+ const rightActual = parsedActual ?? relevant.json;
5988
+ if (!parsedExpected || !rightActual) {
5989
+ return "";
5990
+ }
5991
+ const changes = jsonDiff(parsedExpected, rightActual);
5992
+ if (!changes.length) {
5993
+ return "";
5994
+ }
5995
+ const head = "\n Diff:";
5996
+ const body = changes.map((change) => {
5997
+ const marker = change.kind === "added" ? "+" : change.kind === "removed" ? "-" : "~";
5998
+ const previewText = change.preview ? `: ${ansi.dim(change.preview)}` : "";
5999
+ return `
6000
+ ${marker} ${change.path}${previewText}`;
6001
+ }).join("");
6002
+ return head + body;
6003
+ })();
6004
+ parts.push(line1, expVsAct, whyLines, diffLines, "\n");
6005
+ out.push(...parts.filter(Boolean));
6006
+ } else if (!hasTransport && !relevant && HEADLAMP_HTTP_SHOW_MISS()) {
6007
+ out.push(
6008
+ " HTTP:",
6009
+ `
6010
+ ${ansi.dim("No relevant HTTP exchange found. (HEADLAMP_HTTP_MISS=0 to hide)")}`,
6011
+ "\n"
6012
+ );
6013
+ }
6014
+ }
4992
6015
  }
4993
- out.push(drawFailLine());
4994
- out.push("");
6016
+ const minimalInfo = msgLines.every((ln) => !ln.trim());
6017
+ if (minimalInfo) {
6018
+ try {
6019
+ out.push(...buildThrownSection(assertion.failureDetails || []));
6020
+ } catch {
6021
+ }
6022
+ }
6023
+ out.push(...buildConsoleSection(stripBridgeEventsFromConsole(file.console ?? null)));
6024
+ if (ctx.showStacks && stackPreview.length === 0) {
6025
+ const tail = mergedForStack.filter((ln) => isStackLine(stripAnsiSimple(ln))).slice(-4).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`);
6026
+ if (tail.length) {
6027
+ out.push(ansi.dim(" Stack:"), ...tail, "");
6028
+ }
6029
+ }
6030
+ out.push(drawFailLine(), "");
4995
6031
  }
4996
6032
  }
4997
6033
  const failedCount = data.aggregated.numFailedTests;
4998
- out.push(drawRule(colorTokens.failPill(` Failed Tests ${failedCount} `)));
6034
+ out.push(drawRule(BackgroundColors.Failure(ansi.white(` Failed Tests ${failedCount} `))));
4999
6035
  out.push("");
5000
- const footer = vitestFooter(
5001
- data.aggregated,
5002
- data.aggregated?.startTime ?? data.startTime,
5003
- data.aggregated?.runTimeMs
5004
- );
5005
- return `${out.join("\n")}
5006
- ${footer}`;
5007
- }
6036
+ out.push(vitestFooter(data.aggregated));
6037
+ return out.join("\n");
6038
+ };
6039
+ var tryBridgeFallback = (raw, ctx, opts) => {
6040
+ const bridgeJsonPath = extractBridgePath2(raw, ctx.cwd);
6041
+ if (!bridgeJsonPath || !fs6.existsSync(bridgeJsonPath)) {
6042
+ return null;
6043
+ }
6044
+ try {
6045
+ const json = import_json52.default.parse(fs6.readFileSync(bridgeJsonPath, "utf8"));
6046
+ const bridge = coerceJestJsonToBridge(json);
6047
+ return renderVitestFromJestJSON(bridge, ctx, opts);
6048
+ } catch {
6049
+ return null;
6050
+ }
6051
+ };
6052
+
6053
+ // src/lib/formatJestOutputVitest.ts
6054
+ var formatJestOutputVitest = (raw, opts) => pipe(
6055
+ { raw, opts },
6056
+ (state) => ({
6057
+ ...state,
6058
+ ctx: makeCtx(state.opts, /\bFAIL\b/.test(stripAnsiSimple(state.raw)))
6059
+ }),
6060
+ (state) => ({ ...state, chunks: parseChunks(state.raw) }),
6061
+ (state) => ({
6062
+ ...state,
6063
+ rendered: renderChunks(state.chunks, state.ctx, mkPrettyFns(), {
6064
+ onlyFailures: Boolean(state.opts?.onlyFailures)
6065
+ })
6066
+ }),
6067
+ (state) => {
6068
+ if (state.rendered.hadParsed) {
6069
+ return state.rendered.text;
6070
+ }
6071
+ const fallback = tryBridgeFallback(state.raw, state.ctx, {
6072
+ onlyFailures: Boolean(state.opts?.onlyFailures)
6073
+ });
6074
+ if (!fallback) {
6075
+ return state.rendered.text;
6076
+ }
6077
+ const prefix = state.rendered.text;
6078
+ return prefix ? `${prefix}
6079
+ ${fallback}` : fallback;
6080
+ }
6081
+ );
5008
6082
 
5009
6083
  // src/lib/program.ts
5010
6084
  var jestBin = "./node_modules/.bin/jest";
@@ -5049,7 +6123,7 @@ var mergeLcov = async () => {
5049
6123
  try {
5050
6124
  const entries = fsSync3.readdirSync(dir, { withFileTypes: true });
5051
6125
  for (const entry of entries) {
5052
- const full = path10.join(dir, entry.name);
6126
+ const full = path11.join(dir, entry.name);
5053
6127
  if (entry.isDirectory()) {
5054
6128
  out.push(...collectLcovs(full));
5055
6129
  } else if (entry.isFile() && entry.name === "lcov.info") {
@@ -5061,8 +6135,8 @@ var mergeLcov = async () => {
5061
6135
  return out;
5062
6136
  };
5063
6137
  try {
5064
- const jestRoot = path10.join("coverage", "jest");
5065
- const candidates = [path10.join(jestRoot, "lcov.info"), ...collectLcovs(jestRoot)].map((candidatePath) => path10.resolve(candidatePath)).filter((absolutePath, index, arr) => arr.indexOf(absolutePath) === index);
6138
+ const jestRoot = path11.join("coverage", "jest");
6139
+ const candidates = [path11.join(jestRoot, "lcov.info"), ...collectLcovs(jestRoot)].map((candidatePath) => path11.resolve(candidatePath)).filter((absolutePath, index, arr) => arr.indexOf(absolutePath) === index);
5066
6140
  for (const filePath of candidates) {
5067
6141
  try {
5068
6142
  const content = await readOrEmpty(filePath);
@@ -5102,7 +6176,7 @@ var emitMergedCoverage = async (ui, opts) => {
5102
6176
  try {
5103
6177
  const entries = fsSync3.readdirSync(dir, { withFileTypes: true });
5104
6178
  for (const entry of entries) {
5105
- const full = path10.join(dir, entry.name);
6179
+ const full = path11.join(dir, entry.name);
5106
6180
  if (entry.isDirectory()) {
5107
6181
  out.push(...listJsons(full));
5108
6182
  } else if (entry.isFile() && entry.name === "coverage-final.json") {
@@ -5113,11 +6187,11 @@ var emitMergedCoverage = async (ui, opts) => {
5113
6187
  }
5114
6188
  return out;
5115
6189
  };
5116
- const coverageRoot = path10.join("coverage", "jest");
6190
+ const coverageRoot = path11.join("coverage", "jest");
5117
6191
  const jsonCandidates = [
5118
- path10.join(coverageRoot, "coverage-final.json"),
6192
+ path11.join(coverageRoot, "coverage-final.json"),
5119
6193
  ...listJsons(coverageRoot)
5120
- ].map((candidatePath) => path10.resolve(candidatePath)).filter((absolutePath, index, arr) => {
6194
+ ].map((candidatePath) => path11.resolve(candidatePath)).filter((absolutePath, index, arr) => {
5121
6195
  const isFirst = arr.indexOf(absolutePath) === index;
5122
6196
  const exists = fsSync3.existsSync(absolutePath);
5123
6197
  return isFirst && exists;
@@ -5179,7 +6253,7 @@ var emitMergedCoverage = async (ui, opts) => {
5179
6253
  executedTests: opts.executedTests ?? []
5180
6254
  });
5181
6255
  const context = LibReport.createContext({
5182
- dir: path10.resolve("coverage", "merged"),
6256
+ dir: path11.resolve("coverage", "merged"),
5183
6257
  coverageMap: filteredMap,
5184
6258
  defaultSummarizer: "nested"
5185
6259
  });
@@ -5247,8 +6321,8 @@ var emitMergedCoverage = async (ui, opts) => {
5247
6321
  for (const reporter of reporters) {
5248
6322
  reporter.execute(context);
5249
6323
  }
5250
- const textPath = path10.resolve("coverage", "merged", "coverage.txt");
5251
- const summaryPath = path10.resolve("coverage", "merged", "coverage-summary.txt");
6324
+ const textPath = path11.resolve("coverage", "merged", "coverage.txt");
6325
+ const summaryPath = path11.resolve("coverage", "merged", "coverage-summary.txt");
5252
6326
  const filesToPrint = [];
5253
6327
  if (fsSync3.existsSync(textPath)) {
5254
6328
  filesToPrint.push(textPath);
@@ -5391,13 +6465,13 @@ var program = async () => {
5391
6465
  const rels2 = Array.from(
5392
6466
  /* @__PURE__ */ new Set([...branchDiff, ...stagedNow, ...unstagedNow, ...untrackedNow])
5393
6467
  );
5394
- return rels2.map((rel) => path10.resolve(cwd, rel).replace(/\\/g, "/")).filter((abs) => !abs.includes("/node_modules/") && !abs.includes("/coverage/"));
6468
+ return rels2.map((rel) => path11.resolve(cwd, rel).replace(/\\/g, "/")).filter((abs) => !abs.includes("/node_modules/") && !abs.includes("/coverage/"));
5395
6469
  }
5396
6470
  const staged = mode === "staged" || mode === "all" ? await collect("git", ["diff", "--name-only", "--diff-filter=ACMRTUXB", "--cached"]) : [];
5397
6471
  const unstagedTracked = mode === "unstaged" || mode === "all" ? await collect("git", ["diff", "--name-only", "--diff-filter=ACMRTUXB"]) : [];
5398
6472
  const untracked = mode === "unstaged" || mode === "all" ? await collect("git", ["ls-files", "--others", "--exclude-standard"]) : [];
5399
6473
  const rels = Array.from(/* @__PURE__ */ new Set([...staged, ...unstagedTracked, ...untracked]));
5400
- return rels.map((rel) => path10.resolve(cwd, rel).replace(/\\/g, "/")).filter((abs) => !abs.includes("/node_modules/") && !abs.includes("/coverage/"));
6474
+ return rels.map((rel) => path11.resolve(cwd, rel).replace(/\\/g, "/")).filter((abs) => !abs.includes("/node_modules/") && !abs.includes("/coverage/"));
5401
6475
  };
5402
6476
  const repoRootForChanged = workspaceRoot ?? await findRepoRoot();
5403
6477
  const changedSelectionAbs = changed ? await getChangedFiles(changed, repoRootForChanged) : [];
@@ -5422,18 +6496,18 @@ var program = async () => {
5422
6496
  if (!token) {
5423
6497
  continue;
5424
6498
  }
5425
- const isAbs = path10.isAbsolute(token);
6499
+ const isAbs = path11.isAbsolute(token);
5426
6500
  const looksLikeRelPath = /[\\/]/.test(token);
5427
6501
  let candidateFromRoot;
5428
6502
  if (token.startsWith("/")) {
5429
- candidateFromRoot = path10.join(repoRoot, token.slice(1));
6503
+ candidateFromRoot = path11.join(repoRoot, token.slice(1));
5430
6504
  } else if (looksLikeRelPath) {
5431
- candidateFromRoot = path10.join(repoRoot, token);
6505
+ candidateFromRoot = path11.join(repoRoot, token);
5432
6506
  } else {
5433
6507
  candidateFromRoot = void 0;
5434
6508
  }
5435
6509
  const tryPushIfProd = (absPath) => {
5436
- const norm = path10.resolve(absPath).replace(/\\/g, "/");
6510
+ const norm = path11.resolve(absPath).replace(/\\/g, "/");
5437
6511
  const isTest = /(^|\/)tests?\//i.test(norm) || /\.(test|spec)\.[tj]sx?$/i.test(norm);
5438
6512
  if (!isTest && fsSync3.existsSync(norm)) {
5439
6513
  results.add(norm);
@@ -5455,7 +6529,7 @@ var program = async () => {
5455
6529
  }),
5456
6530
  timeoutMs: 4e3
5457
6531
  });
5458
- const matches = out.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((rel) => path10.resolve(repoRoot, rel).replace(/\\/g, "/")).filter(
6532
+ const matches = out.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((rel) => path11.resolve(repoRoot, rel).replace(/\\/g, "/")).filter(
5459
6533
  (abs) => !abs.includes("/node_modules/") && !abs.includes("/coverage/") && !/(^|\/)tests?\//i.test(abs) && !/\.(test|spec)\.[tj]sx?$/i.test(abs)
5460
6534
  );
5461
6535
  matches.forEach((abs) => results.add(abs));
@@ -5476,8 +6550,8 @@ var program = async () => {
5476
6550
  const jestDiscoveryArgs = selectionIncludesProdPaths ? stripPathTokens(jest) : jest;
5477
6551
  const projectConfigs = [];
5478
6552
  try {
5479
- const baseCfg = path10.resolve("jest.config.js");
5480
- const tsCfg = path10.resolve("jest.ts.config.js");
6553
+ const baseCfg = path11.resolve("jest.config.js");
6554
+ const tsCfg = path11.resolve("jest.ts.config.js");
5481
6555
  if (fsSync3.existsSync(baseCfg)) {
5482
6556
  projectConfigs.push(baseCfg);
5483
6557
  }
@@ -5494,7 +6568,7 @@ var program = async () => {
5494
6568
  );
5495
6569
  const prodSelections2 = expandedProdSelections;
5496
6570
  for (const cfg of projectConfigs) {
5497
- const cfgCwd = path10.dirname(cfg);
6571
+ const cfgCwd = path11.dirname(cfg);
5498
6572
  const allTests = await discoverJestResilient([...jestDiscoveryArgs, "--config", cfg], {
5499
6573
  cwd: cfgCwd
5500
6574
  });
@@ -5507,7 +6581,7 @@ var program = async () => {
5507
6581
  });
5508
6582
  } catch (err) {
5509
6583
  if (isDebug()) {
5510
- console.warn(`direct selection failed for project ${path10.basename(cfg)}: ${String(err)}`);
6584
+ console.warn(`direct selection failed for project ${path11.basename(cfg)}: ${String(err)}`);
5511
6585
  }
5512
6586
  }
5513
6587
  perProjectFiles.set(cfg, directPerProject);
@@ -5519,7 +6593,7 @@ var program = async () => {
5519
6593
  )} | related=${selectionIncludesProdPaths} | cwd=${repoRootForDiscovery}`
5520
6594
  );
5521
6595
  for (const cfg of projectConfigs) {
5522
- const cfgCwd = path10.dirname(cfg);
6596
+ const cfgCwd = path11.dirname(cfg);
5523
6597
  const files = await discoverJestResilient([...jestDiscoveryArgs, "--config", cfg], {
5524
6598
  cwd: cfgCwd
5525
6599
  });
@@ -5534,13 +6608,13 @@ var program = async () => {
5534
6608
  );
5535
6609
  const candidates = selectionHasPaths && selectionLooksLikeTest ? selectionTestPaths : files;
5536
6610
  const absFiles = candidates.map(
5537
- (candidatePath) => path10.isAbsolute(candidatePath) ? candidatePath : path10.join(repoRootForDiscovery, candidatePath)
6611
+ (candidatePath) => path11.isAbsolute(candidatePath) ? candidatePath : path11.join(repoRootForDiscovery, candidatePath)
5538
6612
  ).map((absolutePath) => absolutePath.replace(/\\/g, "/"));
5539
6613
  const onlyOwned = await filterCandidatesForProject(
5540
6614
  cfg,
5541
6615
  jestDiscoveryArgs,
5542
6616
  absFiles,
5543
- path10.dirname(cfg)
6617
+ path11.dirname(cfg)
5544
6618
  );
5545
6619
  perProjectFiltered.set(cfg, onlyOwned);
5546
6620
  }
@@ -5552,7 +6626,7 @@ var program = async () => {
5552
6626
  if (selectionHasPaths && prodSelections.length > 0) {
5553
6627
  console.info(`rg related \u2192 prodSelections=${prodSelections.length} (starting)`);
5554
6628
  const repoRootForRefinement = workspaceRoot ?? await findRepoRoot();
5555
- const selectionKey = prodSelections.map((absPath) => path10.relative(repoRootForRefinement, absPath).replace(/\\/g, "/")).sort((firstPath, secondPath) => firstPath.localeCompare(secondPath)).join("|");
6629
+ const selectionKey = prodSelections.map((absPath) => path11.relative(repoRootForRefinement, absPath).replace(/\\/g, "/")).sort((firstPath, secondPath) => firstPath.localeCompare(secondPath)).join("|");
5556
6630
  const { cachedRelated: cachedRelated2, findRelatedTestsFast: findRelatedTestsFast2, DEFAULT_TEST_GLOBS: DEFAULT_TEST_GLOBS2 } = await Promise.resolve().then(() => (init_fast_related(), fast_related_exports));
5557
6631
  const { DEFAULT_EXCLUDE: DEFAULT_EXCLUDE2 } = await Promise.resolve().then(() => (init_args(), args_exports));
5558
6632
  const rgMatches = await cachedRelated2({
@@ -5582,7 +6656,7 @@ var program = async () => {
5582
6656
  cfg,
5583
6657
  jestDiscoveryArgs,
5584
6658
  rgCandidates,
5585
- path10.dirname(cfg)
6659
+ path11.dirname(cfg)
5586
6660
  );
5587
6661
  perProjectFromRg.set(cfg, owned);
5588
6662
  }
@@ -5597,9 +6671,9 @@ var program = async () => {
5597
6671
  } else {
5598
6672
  const repoRootForScan = repoRootForDiscovery;
5599
6673
  const toSeeds = (abs) => {
5600
- const rel = path10.relative(repoRootForScan, abs).replace(/\\/g, "/");
6674
+ const rel = path11.relative(repoRootForScan, abs).replace(/\\/g, "/");
5601
6675
  const withoutExt = rel.replace(/\.(m?[tj]sx?)$/i, "");
5602
- const base = path10.basename(withoutExt);
6676
+ const base = path11.basename(withoutExt);
5603
6677
  const segs = withoutExt.split("/");
5604
6678
  const tail2 = segs.slice(-2).join("/");
5605
6679
  return Array.from(new Set([withoutExt, base, tail2].filter(Boolean)));
@@ -5614,8 +6688,8 @@ var program = async () => {
5614
6688
  }
5615
6689
  };
5616
6690
  const resolveLocalImport = (fromFile, spec) => {
5617
- const baseDir = path10.dirname(fromFile);
5618
- const cand = path10.resolve(baseDir, spec);
6691
+ const baseDir = path11.dirname(fromFile);
6692
+ const cand = path11.resolve(baseDir, spec);
5619
6693
  const exts = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
5620
6694
  for (const ext of exts) {
5621
6695
  const full = ext ? `${cand}${ext}` : cand;
@@ -5624,7 +6698,7 @@ var program = async () => {
5624
6698
  }
5625
6699
  }
5626
6700
  for (const ext of exts) {
5627
- const full = path10.join(cand, `index${ext}`);
6701
+ const full = path11.join(cand, `index${ext}`);
5628
6702
  if (fsSync3.existsSync(full)) {
5629
6703
  return full;
5630
6704
  }
@@ -5678,7 +6752,7 @@ var program = async () => {
5678
6752
  cfg,
5679
6753
  jestDiscoveryArgs,
5680
6754
  keptCandidates,
5681
- path10.dirname(cfg)
6755
+ path11.dirname(cfg)
5682
6756
  );
5683
6757
  perProjectFromScan.set(cfg, owned);
5684
6758
  }
@@ -5708,7 +6782,7 @@ var program = async () => {
5708
6782
  try {
5709
6783
  const allAcross = [];
5710
6784
  for (const cfg of projectConfigs) {
5711
- const cfgCwd = path10.dirname(cfg);
6785
+ const cfgCwd = path11.dirname(cfg);
5712
6786
  const listed = await discoverJestResilient([...jestDiscoveryArgs, "--config", cfg], {
5713
6787
  cwd: cfgCwd
5714
6788
  });
@@ -5722,23 +6796,23 @@ var program = async () => {
5722
6796
  }
5723
6797
  }
5724
6798
  const seeds = prodSelections.map(
5725
- (abs) => path10.relative(repoRoot, abs).replace(/\\/g, "/").replace(/\.(m?[tj]sx?)$/i, "")
6799
+ (abs) => path11.relative(repoRoot, abs).replace(/\\/g, "/").replace(/\.(m?[tj]sx?)$/i, "")
5726
6800
  ).flatMap((rel) => {
5727
- const base = path10.basename(rel);
6801
+ const base = path11.basename(rel);
5728
6802
  const segments = rel.split("/");
5729
6803
  return Array.from(new Set([rel, base, segments.slice(-2).join("/")].filter(Boolean)));
5730
6804
  });
5731
6805
  const includesSeed = (text) => seeds.some((seed) => text.includes(seed));
5732
6806
  const tryReadFile = async (absPath) => {
5733
6807
  try {
5734
- return await fs5.readFile(absPath, "utf8");
6808
+ return await fs7.readFile(absPath, "utf8");
5735
6809
  } catch {
5736
6810
  return "";
5737
6811
  }
5738
6812
  };
5739
6813
  const resolveLocalImport = (fromFile, spec) => {
5740
- const baseDir = path10.dirname(fromFile);
5741
- const candidate = path10.resolve(baseDir, spec);
6814
+ const baseDir = path11.dirname(fromFile);
6815
+ const candidate = path11.resolve(baseDir, spec);
5742
6816
  const extensions = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"];
5743
6817
  for (const ext of extensions) {
5744
6818
  const fullPath = ext ? `${candidate}${ext}` : candidate;
@@ -5747,7 +6821,7 @@ var program = async () => {
5747
6821
  }
5748
6822
  }
5749
6823
  for (const ext of extensions) {
5750
- const fullPath = path10.join(candidate, `index${ext}`);
6824
+ const fullPath = path11.join(candidate, `index${ext}`);
5751
6825
  if (fsSync3.existsSync(fullPath)) {
5752
6826
  return fullPath;
5753
6827
  }
@@ -5904,10 +6978,10 @@ var program = async () => {
5904
6978
  };
5905
6979
  const prodSeedsForRun = (() => {
5906
6980
  const changedAbs = (changedSelectionAbs ?? []).map(
5907
- (absPath) => path10.resolve(absPath).replace(/\\/g, "/")
6981
+ (absPath) => path11.resolve(absPath).replace(/\\/g, "/")
5908
6982
  );
5909
6983
  const selAbs = selectionPathsAugmented.map(
5910
- (pathToken) => path10.resolve(pathToken).replace(/\\/g, "/")
6984
+ (pathToken) => path11.resolve(pathToken).replace(/\\/g, "/")
5911
6985
  );
5912
6986
  return (changedAbs.length ? changedAbs : selAbs).filter(
5913
6987
  (abs) => /[\\/]/.test(abs) && !/(^|\/)tests?\//i.test(abs) && !/\.(test|spec)\.[tj]sx?$/i.test(abs)
@@ -5922,29 +6996,50 @@ var program = async () => {
5922
6996
  const cfg = projectsToRun[projIndex];
5923
6997
  const files = perProjectFiltered.get(cfg) ?? [];
5924
6998
  if (files.length === 0) {
5925
- console.info(`Project ${path10.basename(cfg)}: 0 matching tests after filter; skipping.`);
6999
+ console.info(`Project ${path11.basename(cfg)}: 0 matching tests after filter; skipping.`);
5926
7000
  continue;
5927
7001
  }
5928
7002
  files.forEach(
5929
- (absTestPath) => executedTestFilesSet.add(path10.resolve(absTestPath).replace(/\\/g, "/"))
7003
+ (absTestPath) => executedTestFilesSet.add(path11.resolve(absTestPath).replace(/\\/g, "/"))
5930
7004
  );
5931
- const outJson = path10.join(
5932
- os2.tmpdir(),
7005
+ const outJson = path11.join(
7006
+ os3.tmpdir(),
5933
7007
  `jest-bridge-${Date.now()}-${Math.random().toString(36).slice(2)}.json`
5934
7008
  );
5935
- const reporterPath = path10.resolve("scripts/jest-vitest-bridge.cjs");
7009
+ const reporterPath = path11.resolve("scripts/jest-vitest-bridge.cjs");
5936
7010
  try {
5937
- if (!fsSync3.existsSync(reporterPath)) {
5938
- fsSync3.mkdirSync(path10.dirname(reporterPath), { recursive: true });
7011
+ const needsWrite = (() => {
7012
+ try {
7013
+ const existing = fsSync3.readFileSync(reporterPath, "utf8");
7014
+ return existing !== JEST_BRIDGE_REPORTER_SOURCE;
7015
+ } catch {
7016
+ return true;
7017
+ }
7018
+ })();
7019
+ if (needsWrite) {
7020
+ fsSync3.mkdirSync(path11.dirname(reporterPath), { recursive: true });
5939
7021
  fsSync3.writeFileSync(reporterPath, JEST_BRIDGE_REPORTER_SOURCE, "utf8");
5940
7022
  }
7023
+ const envPath = path11.resolve("scripts/jest-bridge-env.cjs");
7024
+ try {
7025
+ const existingEnv = fsSync3.readFileSync(envPath, "utf8");
7026
+ if (existingEnv !== JEST_BRIDGE_ENV_SOURCE) {
7027
+ fsSync3.writeFileSync(envPath, JEST_BRIDGE_ENV_SOURCE, "utf8");
7028
+ }
7029
+ } catch {
7030
+ try {
7031
+ fsSync3.mkdirSync(path11.dirname(envPath), { recursive: true });
7032
+ } catch {
7033
+ }
7034
+ fsSync3.writeFileSync(envPath, JEST_BRIDGE_ENV_SOURCE, "utf8");
7035
+ }
5941
7036
  } catch (ensureReporterError) {
5942
7037
  console.warn(`Unable to ensure jest bridge reporter: ${String(ensureReporterError)}`);
5943
7038
  }
5944
- const selectedFilesForCoverage = selectionPathsAugmented.filter((pathToken) => /[\\/]/.test(pathToken)).filter((pathToken) => !looksLikeTestPath(pathToken)).map((pathToken) => path10.relative(repoRootForDiscovery, pathToken).replace(/\\\\/g, "/")).filter((rel) => rel && !/^\.+\//.test(rel)).map((rel) => rel.startsWith("./") ? rel : `./${rel}`);
7039
+ const selectedFilesForCoverage = selectionPathsAugmented.filter((pathToken) => /[\\/]/.test(pathToken)).filter((pathToken) => !looksLikeTestPath(pathToken)).map((pathToken) => path11.relative(repoRootForDiscovery, pathToken).replace(/\\\\/g, "/")).filter((rel) => rel && !/^\.+\//.test(rel)).map((rel) => rel.startsWith("./") ? rel : `./${rel}`);
5945
7040
  const coverageFromArgs = [];
5946
- for (const relPath of selectedFilesForCoverage) {
5947
- coverageFromArgs.push("--collectCoverageFrom", relPath);
7041
+ for (const relPath2 of selectedFilesForCoverage) {
7042
+ coverageFromArgs.push("--collectCoverageFrom", relPath2);
5948
7043
  }
5949
7044
  const { code, output } = await runWithCapture(
5950
7045
  babelNodeBin,
@@ -5954,17 +7049,16 @@ var program = async () => {
5954
7049
  jestBin,
5955
7050
  "--config",
5956
7051
  cfg,
7052
+ "--testLocationInResults",
5957
7053
  "--runTestsByPath",
5958
7054
  `--reporters=${reporterPath}`,
5959
- "--silent",
5960
7055
  "--colors",
5961
- "--json",
5962
- "--outputFile",
5963
- outJson,
7056
+ "--env",
7057
+ path11.resolve("scripts/jest-bridge-env.cjs"),
5964
7058
  ...sanitizedJestRunArgs,
5965
7059
  ...collectCoverage ? [
5966
7060
  "--coverageDirectory",
5967
- path10.join("coverage", "jest", path10.basename(cfg).replace(/[^a-zA-Z0-9_.-]+/g, "_"))
7061
+ path11.join("coverage", "jest", path11.basename(cfg).replace(/[^a-zA-Z0-9_.-]+/g, "_"))
5968
7062
  ] : [],
5969
7063
  ...coverageFromArgs,
5970
7064
  "--passWithNoTests",
@@ -5997,17 +7091,23 @@ var program = async () => {
5997
7091
  ...bridge,
5998
7092
  testResults: sortTestResultsWithRank(fileRank, bridge.testResults).reverse()
5999
7093
  };
6000
- pretty = renderVitestFromJestJSON(reordered, {
6001
- cwd: repoRootForDiscovery,
6002
- ...editorCmd !== void 0 ? { editorCmd } : {},
6003
- onlyFailures
6004
- });
7094
+ pretty = renderVitestFromJestJSON(
7095
+ reordered,
7096
+ makeCtx(
7097
+ { cwd: repoRootForDiscovery, ...editorCmd !== void 0 ? { editorCmd } : {} },
7098
+ /\bFAIL\b/.test(stripAnsiSimple(output))
7099
+ ),
7100
+ { onlyFailures }
7101
+ );
6005
7102
  } catch {
6006
- pretty = renderVitestFromJestJSON(bridge, {
6007
- cwd: repoRootForDiscovery,
6008
- ...editorCmd !== void 0 ? { editorCmd } : {},
6009
- onlyFailures
6010
- });
7103
+ pretty = renderVitestFromJestJSON(
7104
+ bridge,
7105
+ makeCtx(
7106
+ { cwd: repoRootForDiscovery, ...editorCmd !== void 0 ? { editorCmd } : {} },
7107
+ /\bFAIL\b/.test(stripAnsiSimple(output))
7108
+ ),
7109
+ { onlyFailures }
7110
+ );
6011
7111
  }
6012
7112
  if (debug) {
6013
7113
  const preview = pretty.split("\n").slice(0, 3).join("\n");
@@ -6021,18 +7121,31 @@ ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
6021
7121
  console.info(String(jsonErr));
6022
7122
  console.info(`fallback: raw output lines=${output.split(/\r?\n/).length}`);
6023
7123
  }
6024
- const renderOpts = {
7124
+ pretty = formatJestOutputVitest(output, {
6025
7125
  cwd: repoRootForDiscovery,
6026
7126
  ...editorCmd !== void 0 ? { editorCmd } : {},
6027
7127
  onlyFailures
6028
- };
6029
- pretty = formatJestOutputVitest(output, renderOpts);
7128
+ });
6030
7129
  if (debug) {
6031
7130
  const preview = pretty.split("\n").slice(0, 3).join("\n");
6032
7131
  console.info(`pretty preview (text):
6033
7132
  ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
6034
7133
  }
6035
7134
  }
7135
+ try {
7136
+ const looksSparse = /\n\s*Error:\s*\n/.test(pretty) && !/(Message:|Thrown:|Events:|Console errors:)/.test(pretty);
7137
+ if (looksSparse) {
7138
+ const rawAlso = formatJestOutputVitest(output, {
7139
+ cwd: repoRootForDiscovery,
7140
+ ...editorCmd !== void 0 ? { editorCmd } : {},
7141
+ onlyFailures
7142
+ });
7143
+ const merged = `${stripFooter(pretty)}
7144
+ ${stripFooter(rawAlso)}`.trimEnd();
7145
+ pretty = merged;
7146
+ }
7147
+ } catch {
7148
+ }
6036
7149
  pretty = stripFooter(pretty);
6037
7150
  if (pretty.trim().length > 0) {
6038
7151
  process.stdout.write(pretty.endsWith("\n") ? pretty : `${pretty}
@@ -6071,10 +7184,10 @@ ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
6071
7184
  try {
6072
7185
  const prodSeeds = (() => {
6073
7186
  const changedAbs = (changedSelectionAbs ?? []).map(
6074
- (absPath) => path10.resolve(absPath).replace(/\\/g, "/")
7187
+ (absPath) => path11.resolve(absPath).replace(/\\/g, "/")
6075
7188
  );
6076
7189
  const selAbs = selectionPathsAugmented.map(
6077
- (pathToken) => path10.resolve(pathToken).replace(/\\/g, "/")
7190
+ (pathToken) => path11.resolve(pathToken).replace(/\\/g, "/")
6078
7191
  );
6079
7192
  return (changedAbs.length ? changedAbs : selAbs).filter(
6080
7193
  (abs) => /[\\/]/.test(abs) && !/(^|\/)tests?\//i.test(abs) && !/\.(test|spec)\.[tj]sx?$/i.test(abs)
@@ -6088,11 +7201,18 @@ ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
6088
7201
  unified.testResults = ordered;
6089
7202
  } catch {
6090
7203
  }
6091
- const text = renderVitestFromJestJSON(unified, {
6092
- cwd: repoRootForDiscovery,
6093
- ...editorCmd !== void 0 ? { editorCmd } : {},
6094
- onlyFailures
6095
- });
7204
+ const showStacks = Boolean(unified.aggregated?.numFailedTests > 0);
7205
+ let text = renderVitestFromJestJSON(
7206
+ unified,
7207
+ makeCtx(
7208
+ { cwd: repoRootForDiscovery, ...editorCmd !== void 0 ? { editorCmd } : {} },
7209
+ showStacks
7210
+ ),
7211
+ { onlyFailures }
7212
+ );
7213
+ if (onlyFailures) {
7214
+ text = text.split(/\r?\n/).filter((line) => !/^\s*PASS\b/.test(stripAnsiSimple(line))).join("\n");
7215
+ }
6096
7216
  if (text.trim().length > 0) {
6097
7217
  process.stdout.write(text.endsWith("\n") ? text : `${text}
6098
7218
  `);