mejora 2.3.2 → 2.3.3

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.
@@ -0,0 +1,13 @@
1
+ import { a as RawSnapshot } from "./types-B1hGuB2T.mjs";
2
+
3
+ //#region src/check-worker.d.ts
4
+ declare function run({
5
+ checkId
6
+ }: {
7
+ checkId: string;
8
+ }): Promise<{
9
+ duration: number;
10
+ snapshot: RawSnapshot;
11
+ }>;
12
+ //#endregion
13
+ export { run as default };
@@ -0,0 +1 @@
1
+ import{g as e}from"./typescript-C4WyXHBh.mjs";import{n as t,t as n}from"./registry-DhjvvQpC.mjs";let r=null,i=null;const a=new Map;async function o({checkId:o}){r||(i??=(async()=>{r=await e()})(),await i);let s=r?.checks[o];if(!s)throw Error(`Check not found in config: ${o}`);let c=a.get(s.type);if(!c){c=new t,n(c,r);let e=new Set([s.type]);await Promise.all([c.setup(e),c.validate(e)]),a.set(s.type,c)}let l=performance.now(),u=await c.get(s.type).run(s);return{duration:performance.now()-l,snapshot:u}}export{o as default};
package/dist/index.d.mts CHANGED
@@ -1,320 +1,7 @@
1
+ import { a as RawSnapshot, c as Snapshot, i as IssueInput, l as TypeScriptCheckConfig, n as ESLintCheckConfig, o as RegexCheckConfig, r as Issue, s as RegexPattern, t as Config, u as CheckRunner } from "./types-B1hGuB2T.mjs";
1
2
  import * as eslint0 from "eslint";
2
- import { Linter } from "eslint";
3
3
  import * as typescript0 from "typescript";
4
- import { CompilerOptions } from "typescript";
5
4
 
6
- //#region src/types.d.ts
7
- interface Issue {
8
- /**
9
- * 1-indexed column number for display.
10
- */
11
- column: number;
12
- /**
13
- * Relative path from cwd.
14
- */
15
- file: string;
16
- /**
17
- * Hash of canonical representation.
18
- *
19
- * @example "a1b2c3d4e5f6g7h8i9j0"
20
- */
21
- id: string;
22
- /**
23
- * 1-indexed line number for display.
24
- */
25
- line: number;
26
- /**
27
- * The message.
28
- */
29
- message: string;
30
- /**
31
- * Identifier for the issue (rule name, diagnostic code, etc).
32
- *
33
- * @example "no-nested-ternary" (ESLint)
34
- *
35
- * @example "TS2345" (TypeScript)
36
- */
37
- rule: string;
38
- }
39
- /**
40
- * Issue produced by a check runner.
41
- */
42
- type IssueInput = Omit<Issue, "id">;
43
- type SnapshotType = "items";
44
- interface RawSnapshot {
45
- /**
46
- * Snapshot items (each item represents an issue produced by the check).
47
- */
48
- items: IssueInput[];
49
- type: SnapshotType;
50
- }
51
- interface Snapshot {
52
- items: Issue[];
53
- type: SnapshotType;
54
- }
55
- /**
56
- * Configuration for an ESLint check.
57
- *
58
- * @example
59
- * ```ts
60
- * eslint({
61
- * files: ["src/**\/*.{ts,tsx}"],
62
- * overrides: {
63
- * rules: {
64
- * "no-nested-ternary": "error",
65
- * },
66
- * },
67
- * })
68
- * ```
69
- */
70
- interface ESLintCheckConfig {
71
- /**
72
- * Concurrency setting for ESLint.
73
- *
74
- * @see https://eslint.org/blog/2025/08/multithread-linting/#cli-multithreading-support
75
- *
76
- * @default "auto"
77
- */
78
- concurrency?: "auto" | "off" | number;
79
- /**
80
- * Glob patterns for files to lint.
81
- *
82
- * Passed directly to ESLint's `lintFiles()` method.
83
- *
84
- * @example ["src/**\/*.ts", "src/**\/*.tsx"]
85
- */
86
- files: string[];
87
- /**
88
- * ESLint configuration to merge with the base config.
89
- *
90
- * This is passed to ESLint's `overrideConfig` option and merged with
91
- * your existing ESLint configuration.
92
- *
93
- * Can be a single config object or an array of config objects.
94
- *
95
- * @example
96
- * ```ts
97
- * {
98
- * rules: {
99
- * "no-console": "error",
100
- * },
101
- * }
102
- * ```
103
- */
104
- overrides?: Linter.Config | Linter.Config[];
105
- }
106
- /**
107
- * Configuration for a TypeScript diagnostics check.
108
- *
109
- * @example
110
- * ```ts
111
- * typescript({
112
- * overrides: {
113
- * compilerOptions: {
114
- * noImplicitAny: true,
115
- * },
116
- * },
117
- * })
118
- * ```
119
- */
120
- interface TypeScriptCheckConfig {
121
- /**
122
- * Compiler options to merge with the base tsconfig.
123
- *
124
- * These options are merged with (not replacing) the compiler options
125
- * from your tsconfig file.
126
- */
127
- overrides?: {
128
- compilerOptions?: CompilerOptions;
129
- };
130
- /**
131
- * Path to a TypeScript config file.
132
- *
133
- * If not provided, mejora will search for the nearest `tsconfig.json`
134
- * starting from the current working directory.
135
- *
136
- * @example "tsconfig.strict.json"
137
- */
138
- tsconfig?: string;
139
- }
140
- /**
141
- * A regex pattern configuration.
142
- */
143
- interface RegexPattern {
144
- /**
145
- * Human-readable message for matches.
146
- *
147
- * @example "TODO comment found"
148
- *
149
- * @example "console.log statement"
150
- *
151
- * @example (match) => `Found TODO at line ${match.index + 1}`
152
- */
153
- message?: ((match: RegExpExecArray) => string) | string;
154
- /**
155
- * The regex pattern to match.
156
- *
157
- * @example /\/\/\s*TODO:/gi
158
- *
159
- * @example /console\.log/g
160
- */
161
- pattern: RegExp;
162
- /**
163
- * Rule identifier for this pattern.
164
- * If not provided, uses the pattern source as the rule ID.
165
- *
166
- * @example "no-todos"
167
- *
168
- * @example "no-console-log"
169
- */
170
- rule?: string;
171
- }
172
- /**
173
- * Configuration for regex pattern matching check.
174
- */
175
- interface RegexCheckConfig {
176
- /**
177
- * Concurrency for processing files.
178
- *
179
- * @default 10
180
- */
181
- concurrency?: number;
182
- /**
183
- * Array of glob patterns for files to check.
184
- *
185
- * @example ["src/**\/*.ts", "lib/**\/*.js"]
186
- */
187
- files: string[];
188
- /**
189
- * Array of glob patterns to ignore.
190
- *
191
- * @default ["**\/node_modules/**", "**\/dist/**", "**\/.git/**"]
192
- */
193
- ignore?: string[];
194
- /**
195
- * Array of regex patterns to match.
196
- */
197
- patterns: RegexPattern[];
198
- }
199
- type CustomCheckConfig = Record<string, unknown> & {
200
- type: string;
201
- };
202
- type CheckConfig$1 = (ESLintCheckConfig & {
203
- type: "eslint";
204
- }) | (TypeScriptCheckConfig & {
205
- type: "typescript";
206
- }) | CustomCheckConfig;
207
- /**
208
- * mejora configuration.
209
- *
210
- * Define checks to run and track for regressions.
211
- *
212
- * @example
213
- * ```ts
214
- * import { defineConfig, eslint, typescript } from "mejora";
215
- *
216
- * export default defineConfig({
217
- * checks: {
218
- * "eslint > no-nested-ternary": eslint({
219
- * files: ["src/**\/*.{ts,tsx}"],
220
- * overrides: {
221
- * rules: {
222
- * "no-nested-ternary": "error",
223
- * },
224
- * },
225
- * }),
226
- * "typescript": typescript({
227
- * overrides: {
228
- * compilerOptions: {
229
- * noImplicitAny: true,
230
- * },
231
- * },
232
- * }),
233
- * },
234
- * });
235
- * ```
236
- */
237
- interface Config {
238
- /**
239
- * Check definitions.
240
- *
241
- * Each key is a check identifier used in the baseline and output.
242
- * The identifier can contain any characters.
243
- *
244
- * Use `eslint()` and `typescript()` helpers to create check configs.
245
- *
246
- * @example
247
- * ```ts
248
- * {
249
- * "eslint > no-console": eslint({ ... }),
250
- * "typescript": typescriptCheck({ ... }),
251
- * }
252
- * ```
253
- */
254
- checks: Record<string, CheckConfig$1>;
255
- /**
256
- * Runners to register custom check types.
257
- *
258
- * Built-in checks (eslint, typescript) are always available.
259
- *
260
- * @example
261
- * ```ts
262
- * {
263
- * runners: [myCustomRunner()],
264
- * checks: {
265
- * "custom": myCheck({ ... })
266
- * }
267
- * }
268
- * ```
269
- */
270
- runners?: CheckRunner[];
271
- }
272
- //#endregion
273
- //#region src/check-runner.d.ts
274
- /**
275
- * Interface that all check runners must implement.
276
- *
277
- * Each check type (eslint, typescript, custom) implements this interface
278
- * to provide consistent lifecycle hooks for execution.
279
- */
280
- interface CheckRunner<TConfig = unknown> {
281
- /**
282
- * Execute the check and return a snapshot of issues.
283
- *
284
- * @param config - Check-specific configuration
285
- *
286
- * @returns Snapshot containing all issues found
287
- */
288
- run(config: TConfig): Promise<RawSnapshot>;
289
- /**
290
- * Setup any infrastructure needed for this check.
291
- * Called once during runner setup, in parallel with other checks.
292
- *
293
- * Examples: creating cache directories, initializing compilation state.
294
- *
295
- * Optional - not all checks need infrastructure setup.
296
- */
297
- setup?(): Promise<void>;
298
- /**
299
- * Unique identifier for this check type.
300
- * Must match the `type` field in CheckConfig.
301
- *
302
- * @example "eslint"
303
- *
304
- * @example "typescript"
305
- */
306
- readonly type: string;
307
- /**
308
- * Validate that all dependencies for this check are available.
309
- * Called once during runner setup before any checks execute.
310
- *
311
- * Should throw a descriptive error if dependencies are missing.
312
- *
313
- * @throws {Error} If required dependencies are not installed
314
- */
315
- validate?(): Promise<void>;
316
- }
317
- //#endregion
318
5
  //#region src/runners/eslint.d.ts
319
6
  /**
320
7
  * Check runner for ESLint.
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{a as e,c as t,h as n,i as r,l as i,n as a,o,p as s,s as c}from"./typescript-C0a92mCs.mjs";import{mkdir as l}from"node:fs/promises";import{createReadStream as u}from"node:fs";import{createInterface as d}from"node:readline/promises";const f=[`**/node_modules/**`,`**/dist/**`,`**/.git/**`];function p(e,t){if(t?.length)return t;let n=e.map(e=>/^([^*]+\/)/.exec(e)?.[1]).filter(e=>e!==void 0);return[...f,...n.flatMap(e=>f.map(t=>t.replace(/^\*\*\//,e)))]}async function m(e,t,n){let r=Array.from({length:e.length}),i=0;return await Promise.all(Array.from({length:Math.min(t,e.length)},async()=>{for(;i<e.length;){let t=i++;r[t]=await n(e[t])}})),r}var h=class{type=`regex`;async run(n){let r=process.cwd(),{glob:a}=await import(`tinyglobby`),l=p(n.files,n.ignore),f=await a(n.files,{absolute:!1,cwd:r,ignore:l}),h=n.patterns.map(({message:e,pattern:t,rule:n})=>{let r=t.flags.includes(`g`)?t.flags:`${t.flags}g`;return{message:e,regex:new RegExp(t.source,r),ruleText:n??t.source}}),g=s(o(this.type,r),`${e(n)}.json`),_=await t(g),v={},y=await m(f,n.concurrency??10,async e=>{let t=s(r,e),n=await c(t);if(!n)return[];let i=_[e];if(i?.hash===n)return v[e]=i,i.items;try{let r=[],i=d({crlfDelay:1/0,input:u(t,{encoding:`utf8`})}),a=0;try{for await(let t of i){a++;for(let n of h){n.regex.lastIndex=0;let i;for(;(i=n.regex.exec(t))!==null;){let t=i.index+1,o=typeof n.message==`function`?n.message(i):n.message??`Pattern matched: ${i[0]}`;r.push({column:t,file:e,line:a,message:o,rule:n.ruleText})}}}}finally{i.close()}return v[e]={hash:n,items:r},r}catch{return[]}}),b=[];for(let e of y)b.push(...e);return await i(g,v),{items:b,type:`items`}}async setup(){let e=process.cwd();await l(o(this.type,e),{recursive:!0})}async validate(){try{await import(`tinyglobby`)}catch{throw Error(`${this.type} check requires "tinyglobby" package to be installed. Run: npm install tinyglobby`)}}};const g=()=>new h;function _(e){return{type:`regex`,...e}}export{n as defineConfig,r as eslint,r as eslintCheck,_ as regex,_ as regexCheck,g as regexRunner,a as typescript,a as typescriptCheck};
1
+ import{a as e,c as t,h as n,i as r,l as i,n as a,o,p as s,s as c}from"./typescript-C4WyXHBh.mjs";import{mkdir as l}from"node:fs/promises";import{createReadStream as u}from"node:fs";import{createInterface as d}from"node:readline/promises";const f=[`**/node_modules/**`,`**/dist/**`,`**/.git/**`];function p(e,t){if(t?.length)return t;let n=e.map(e=>/^([^*]+\/)/.exec(e)?.[1]).filter(e=>e!==void 0);return[...f,...n.flatMap(e=>f.map(t=>t.replace(/^\*\*\//,e)))]}async function m(e,t,n){let r=Array.from({length:e.length}),i=0;return await Promise.all(Array.from({length:Math.min(t,e.length)},async()=>{for(;i<e.length;){let t=i++;r[t]=await n(e[t])}})),r}var h=class{type=`regex`;async run(n){let r=process.cwd(),{glob:a}=await import(`tinyglobby`),l=p(n.files,n.ignore),f=await a(n.files,{absolute:!1,cwd:r,ignore:l}),h=n.patterns.map(({message:e,pattern:t,rule:n})=>{let r=t.flags.includes(`g`)?t.flags:`${t.flags}g`;return{message:e,regex:new RegExp(t.source,r),ruleText:n??t.source}}),g=s(o(this.type,r),`${e(n)}.json`),_=await t(g),v={},y=await m(f,n.concurrency??10,async e=>{let t=s(r,e),n=await c(t);if(!n)return[];let i=_[e];if(i?.hash===n)return v[e]=i,i.items;try{let r=[],i=d({crlfDelay:1/0,input:u(t,{encoding:`utf8`})}),a=0;try{for await(let t of i){a++;for(let n of h){n.regex.lastIndex=0;let i;for(;(i=n.regex.exec(t))!==null;){let t=i.index+1,o=typeof n.message==`function`?n.message(i):n.message??`Pattern matched: ${i[0]}`;r.push({column:t,file:e,line:a,message:o,rule:n.ruleText})}}}}finally{i.close()}return v[e]={hash:n,items:r},r}catch{return[]}}),b=[];for(let e of y)b.push(...e);return await i(g,v),{items:b,type:`items`}}async setup(){let e=process.cwd();await l(o(this.type,e),{recursive:!0})}async validate(){try{await import(`tinyglobby`)}catch{throw Error(`${this.type} check requires "tinyglobby" package to be installed. Run: npm install tinyglobby`)}}};const g=()=>new h;function _(e){return{type:`regex`,...e}}export{n as defineConfig,r as eslint,r as eslintCheck,_ as regex,_ as regexCheck,g as regexRunner,a as typescript,a as typescriptCheck};
@@ -0,0 +1 @@
1
+ import{r as e,t}from"./typescript-C4WyXHBh.mjs";var n=class{runners=new Map;static getRequiredTypes(e){return new Set(Object.values(e).map(e=>e.type))}get(e){let t=this.runners.get(e);if(!t)throw Error(`Unknown check type: ${e}`);return t}getTypes(){return new Set(this.runners.keys())}has(e){return this.runners.has(e)}register(e){if(this.runners.has(e.type))throw Error(`Check runner already registered: ${e.type}`);this.runners.set(e.type,e)}async setup(e){await this.runLifecycle(e,`setup`)}async validate(e){await this.runLifecycle(e,`validate`)}async runLifecycle(e,t){let n=[];for(let r of e){let e=this.get(r)[t]?.();e&&n.push(e)}await Promise.all(n)}};function r(n,r){if(n.register(new e),n.register(new t),r.runners)for(let e of r.runners)n.register(e)}export{n,r as t};
package/dist/run.mjs CHANGED
@@ -1,27 +1,27 @@
1
1
  #!/usr/bin/env node
2
- import{d as e,f as t,g as n,m as r,r as i,t as a,u as o}from"./typescript-C0a92mCs.mjs";import{mkdir as s,readFile as c,writeFile as l}from"node:fs/promises";import{inspect as u,parseArgs as d,styleText as f}from"node:util";import{env as p}from"node:process";var m=class{runners=new Map;static getRequiredTypes(e){return new Set(Object.values(e).map(e=>e.type))}get(e){let t=this.runners.get(e);if(!t)throw Error(`Unknown check type: ${e}`);return t}getTypes(){return new Set(this.runners.keys())}has(e){return this.runners.has(e)}register(e){if(this.runners.has(e.type))throw Error(`Check runner already registered: ${e.type}`);this.runners.set(e.type,e)}async setup(e){await this.runLifecycle(e,`setup`)}async validate(e){await this.runLifecycle(e,`validate`)}async runLifecycle(e,t){let n=[];for(let r of e){let e=this.get(r)[t]?.();e&&n.push(e)}await Promise.all(n)}};function h(e,t){if(!(e===void 0||t===0))return e/t}function g(e){let{results:t,totalDuration:n}=e,r=t.length,i=[],a=[],o=[],s=[],c=0,l=0,u=0,d=0,f=0,p=[];for(let e of t){let t=e.snapshot.items.length;f+=t,e.isInitial?(u+=t,o.push(e.checkId)):(e.hasImprovement&&(c+=e.removedIssues.length,i.push(e.checkId)),e.hasRegression&&(l+=e.newIssues.length,a.push(e.checkId)),!e.hasImprovement&&!e.hasRegression&&(d+=t,s.push(e.checkId))),p.push({checkId:e.checkId,duration:e.duration,hasImprovement:e.hasImprovement,hasRegression:e.hasRegression,isInitial:e.isInitial,newIssues:e.newIssues,removedIssues:e.removedIssues,totalIssues:t})}let m={checks:p,exitCode:e.exitCode,hasImprovement:e.hasImprovement,hasRegression:e.hasRegression,summary:{avgDuration:h(n,r),checksRun:r,improvementChecks:i,improvements:c,initial:u,initialChecks:o,regressionChecks:a,regressions:l,totalIssues:f,unchanged:d,unchangedChecks:s},totalDuration:n};return JSON.stringify(m,null,2)}const _=e=>t=>f(e,typeof t==`number`?t.toString():t),v=_(`blue`),y=_(`bold`),b=_(`cyan`),x=_(`dim`),S=_(`green`),C=_(`red`),w=_(`gray`),ee=_(`underline`),te=_(`yellow`);function ne(e){if(e<1e3)return`${e}ms`;let t=e/1e3;if(t<60)return t%1==0?`${t}s`:`${t.toFixed(1)}s`;let n=e/6e4;if(n<60)return n%1==0?`${n}m`:`${n.toFixed(1)}m`;let r=e/36e5;return r%1==0?`${r}h`:`${r.toFixed(1)}h`}function T(e){let t=Math.round(e);return t<1?`<1ms`:ne(t)}function E(e,t){return e===1?t:`${t}s`}const D=` `,re=`${D} `;function ie(e){return e===`initial`?x(`→`):e===`improvement`?S(`↑`):C(`↓`)}function ae(n,r,i){let a=t(n),o=e(n),s=r>0?`:${r}:${i>0?i:1}`:``;return`${a===`.`?``:x(`${a}/`)}${ee(o)}${s?x(s):``}`}function oe(e,t){return[`${ie(t)} ${ae(e.file,e.line,e.column)} ${x(e.rule)}`,e.message]}function O(e,t){let n=[],r=e.slice(0,10);for(let e of r){let[r,i]=oe(e,t);n.push(`${D}${r}`,`${re}${i}`)}let i=e.length-r.length;return i>0&&n.push(`${D}${x(`... and ${i} more`)}`),n}function se(e){return e===void 0?[]:[` ${x(`Duration`)} ${T(e)}`]}function ce(e){return[` ${x(`Issues`)} ${y(e)}`]}function k(e){return[...se(e.duration),...ce(e.snapshot.items.length)]}function le(e){if(!e.hasRegression)return[];let t=e.newIssues.length;return[` ${C(t)} new ${E(t,`issue`)} (${E(t,`regression`)}):`,...O(e.newIssues,`regression`)]}function ue(e){if(!e.hasImprovement)return[];let t=e.removedIssues.length;return[` ${S(t)} ${E(t,`issue`)} fixed (${E(t,`improvement`)}):`,...O(e.removedIssues,`improvement`)]}function de(e,t){let n=t?``:`
3
- `,r=e.snapshot.items.length,i=[`${n}${v(`ℹ`)} ${e.checkId}:`,` Initial baseline created with ${v(r)} ${E(r,`issue`)}`];return e.snapshot.items.length>0&&i.push(...O(e.snapshot.items,`initial`)),i.push(``,...k(e)),i}function fe(e,t){return[`${t?``:`
4
- `}${e.hasRegression?C(`✖`):S(`✔`)} ${e.checkId}:`,...le(e),...ue(e),``,...k(e)]}function pe(e,t){let n=t?``:`
5
- `,r=e.snapshot.items.length;return e.duration===void 0?[`${n}${w(`ℹ`)} ${e.checkId} (${y(r)})`]:[`${n}${w(`ℹ`)} ${e.checkId} (${y(r)}) ${x(T(e.duration))}`]}function me(e,t){return e.isInitial?de(e,t):e.hasRegression||e.hasImprovement?fe(e,t):pe(e,t)}function he(e,t,n){return t?v(`✔ Initial baseline created successfully`):e.hasRegression?n?te(`⚠ Regressions detected (forced)`):`${C(`✗ Regressions detected`)} - Run failed`:e.hasImprovement?`${S(`✔ Improvements detected`)} - Baseline updated`:S(`✔ All checks passed`)}function ge(e,t){let n={hasAnyInitial:!1,totalImprovements:0,totalInitial:0,totalIssues:0,totalRegressions:0};for(let t of e.results){let e=t.snapshot.items.length;if(n.totalIssues+=e,t.isInitial){n.hasAnyInitial=!0,n.totalInitial+=e;continue}let{hasImprovement:r,hasRegression:i}=t;r&&(n.totalImprovements+=t.removedIssues.length),i&&(n.totalRegressions+=t.newIssues.length)}let r=[` ${x(`Improvements`)} ${S(n.totalImprovements)}`,` ${x(`Regressions`)} ${C(n.totalRegressions)}`,` ${x(`Initial`)} ${v(n.totalInitial)}`,` ${x(`Checks`)} ${e.results.length}`,` ${x(`Issues`)} ${y(n.totalIssues)}`],i=h(e.totalDuration,e.results.length);return e.totalDuration!==void 0&&i!==void 0&&r.push(` ${x(`Duration`)} ${T(e.totalDuration)} ${w(`(avg ${T(i)})`)}`),r.push(``,he(e,n.hasAnyInitial,t)),r.join(`
6
- `)}function A(e,t){let n=[];for(let[t,r]of e.results.entries())n.push(...me(r,t===0));return n.length>0&&n.push(``),n.push(ge(e,t)),n.join(`
7
- `)}const j=e=>e in p&&p[e]!==`0`&&p[e]!==`false`;var M=j(`CI`)||j(`CONTINUOUS_INTEGRATION`);function N(e,t){let n=e;for(let e=0;e<t;e++){let e=n.trimEnd();if(!e.endsWith(`}`))break;n=e.slice(0,-1)}return n}function P(e,t){return`${e}${`
8
- }`.repeat(t)}`}function F(e){let t=(e.match(/\{/g)?.length??0)-(e.match(/\}/g)?.length??0);return t===0?e:t<0?N(e,-t):P(e,t)}function I(e){try{return JSON.parse(e)}catch{return}}function L(e){let t=e.trim();return t.endsWith(`,`)?t.slice(0,-1):t}function _e(e){return`{
2
+ import{d as e,f as t,g as n,m as r,u as i}from"./typescript-C4WyXHBh.mjs";import{n as a,t as o}from"./registry-DhjvvQpC.mjs";import{fileURLToPath as s}from"node:url";import{mkdir as c,readFile as l,writeFile as u}from"node:fs/promises";import{inspect as d,parseArgs as f,styleText as p}from"node:util";import{Tinypool as m}from"tinypool";import{env as h}from"node:process";function g(e,t){if(!(e===void 0||t===0))return e/t}function ee(e){let{results:t,totalDuration:n}=e,r=t.length,i=[],a=[],o=[],s=[],c=0,l=0,u=0,d=0,f=0,p=[];for(let e of t){let t=e.snapshot.items.length;f+=t,e.isInitial?(u+=t,o.push(e.checkId)):(e.hasImprovement&&(c+=e.removedIssues.length,i.push(e.checkId)),e.hasRegression&&(l+=e.newIssues.length,a.push(e.checkId)),!e.hasImprovement&&!e.hasRegression&&(d+=t,s.push(e.checkId))),p.push({checkId:e.checkId,duration:e.duration,hasImprovement:e.hasImprovement,hasRegression:e.hasRegression,isInitial:e.isInitial,newIssues:e.newIssues,removedIssues:e.removedIssues,totalIssues:t})}let m={checks:p,exitCode:e.exitCode,hasImprovement:e.hasImprovement,hasRegression:e.hasRegression,summary:{avgDuration:g(n,r),checksRun:r,improvementChecks:i,improvements:c,initial:u,initialChecks:o,regressionChecks:a,regressions:l,totalIssues:f,unchanged:d,unchangedChecks:s},totalDuration:n};return JSON.stringify(m,null,2)}const _=e=>t=>p(e,typeof t==`number`?t.toString():t),v=_(`blue`),y=_(`bold`),te=_(`cyan`),b=_(`dim`),x=_(`green`),S=_(`red`),C=_(`gray`),ne=_(`underline`),re=_(`yellow`);function ie(e){if(e<1e3)return`${e}ms`;let t=e/1e3;if(t<60)return t%1==0?`${t}s`:`${t.toFixed(1)}s`;let n=e/6e4;if(n<60)return n%1==0?`${n}m`:`${n.toFixed(1)}m`;let r=e/36e5;return r%1==0?`${r}h`:`${r.toFixed(1)}h`}function w(e){let t=Math.round(e);return t<1?`<1ms`:ie(t)}function T(e,t){return e===1?t:`${t}s`}const E=` `,ae=`${E} `;function oe(e){return e===`initial`?b(`→`):e===`improvement`?x(`↑`):S(`↓`)}function se(n,r,i){let a=t(n),o=e(n),s=r>0?`:${r}:${i>0?i:1}`:``;return`${a===`.`?``:b(`${a}/`)}${ne(o)}${s?b(s):``}`}function ce(e,t){return[`${oe(t)} ${se(e.file,e.line,e.column)} ${b(e.rule)}`,e.message]}function D(e,t){let n=[],r=e.slice(0,10);for(let e of r){let[r,i]=ce(e,t);n.push(`${E}${r}`,`${ae}${i}`)}let i=e.length-r.length;return i>0&&n.push(`${E}${b(`... and ${i} more`)}`),n}function le(e){return e===void 0?[]:[` ${b(`Duration`)} ${w(e)}`]}function ue(e){return[` ${b(`Issues`)} ${y(e)}`]}function O(e){return[...le(e.duration),...ue(e.snapshot.items.length)]}function de(e){if(!e.hasRegression)return[];let t=e.newIssues.length;return[` ${S(t)} new ${T(t,`issue`)} (${T(t,`regression`)}):`,...D(e.newIssues,`regression`)]}function fe(e){if(!e.hasImprovement)return[];let t=e.removedIssues.length;return[` ${x(t)} ${T(t,`issue`)} fixed (${T(t,`improvement`)}):`,...D(e.removedIssues,`improvement`)]}function pe(e,t){let n=t?``:`
3
+ `,r=e.snapshot.items.length,i=[`${n}${v(`ℹ`)} ${e.checkId}:`,` Initial baseline created with ${v(r)} ${T(r,`issue`)}`];return e.snapshot.items.length>0&&i.push(...D(e.snapshot.items,`initial`)),i.push(``,...O(e)),i}function me(e,t){return[`${t?``:`
4
+ `}${e.hasRegression?S(`✖`):x(`✔`)} ${e.checkId}:`,...de(e),...fe(e),``,...O(e)]}function he(e,t){let n=t?``:`
5
+ `,r=e.snapshot.items.length;return e.duration===void 0?[`${n}${C(`ℹ`)} ${e.checkId} (${y(r)})`]:[`${n}${C(`ℹ`)} ${e.checkId} (${y(r)}) ${b(w(e.duration))}`]}function ge(e,t){return e.isInitial?pe(e,t):e.hasRegression||e.hasImprovement?me(e,t):he(e,t)}function _e(e,t,n){return t?v(`✔ Initial baseline created successfully`):e.hasRegression?n?re(`⚠ Regressions detected (forced)`):`${S(`✗ Regressions detected`)} - Run failed`:e.hasImprovement?`${x(`✔ Improvements detected`)} - Baseline updated`:x(`✔ All checks passed`)}function ve(e,t){let n={hasAnyInitial:!1,totalImprovements:0,totalInitial:0,totalIssues:0,totalRegressions:0};for(let t of e.results){let e=t.snapshot.items.length;if(n.totalIssues+=e,t.isInitial){n.hasAnyInitial=!0,n.totalInitial+=e;continue}let{hasImprovement:r,hasRegression:i}=t;r&&(n.totalImprovements+=t.removedIssues.length),i&&(n.totalRegressions+=t.newIssues.length)}let r=[` ${b(`Improvements`)} ${x(n.totalImprovements)}`,` ${b(`Regressions`)} ${S(n.totalRegressions)}`,` ${b(`Initial`)} ${v(n.totalInitial)}`,` ${b(`Checks`)} ${e.results.length}`,` ${b(`Issues`)} ${y(n.totalIssues)}`],i=g(e.totalDuration,e.results.length);return e.totalDuration!==void 0&&i!==void 0&&r.push(` ${b(`Duration`)} ${w(e.totalDuration)} ${C(`(avg ${w(i)})`)}`),r.push(``,_e(e,n.hasAnyInitial,t)),r.join(`
6
+ `)}function ye(e,t){let n=[];for(let[t,r]of e.results.entries())n.push(...ge(r,t===0));return n.length>0&&n.push(``),n.push(ve(e,t)),n.join(`
7
+ `)}const k=e=>e in h&&h[e]!==`0`&&h[e]!==`false`;var A=k(`CI`)||k(`CONTINUOUS_INTEGRATION`);function j(e,t){let n=e;for(let e=0;e<t;e++){let e=n.trimEnd();if(!e.endsWith(`}`))break;n=e.slice(0,-1)}return n}function M(e,t){return`${e}${`
8
+ }`.repeat(t)}`}function N(e){let t=(e.match(/\{/g)?.length??0)-(e.match(/\}/g)?.length??0);return t===0?e:t<0?j(e,-t):M(e,t)}function P(e){try{return JSON.parse(e)}catch{return}}function F(e){let t=e.trim();return t.endsWith(`,`)?t.slice(0,-1):t}function be(e){return`{
9
9
  "version": 2,
10
- ${L(e)}
11
- }`}function R(e){if(typeof e!=`object`||!e)throw TypeError(`Baseline must be an object`);if(`checks`in e&&e.checks&&typeof e.checks==`object`)return{checks:e.checks,version:2};let t={};for(let[n,r]of Object.entries(e))n!==`version`&&(t[n]=r);return{checks:t,version:2}}function z(e){try{let t=I(e.trim());if(t)return R(t);let n=_e(F(L(e)));return R(JSON.parse(n))}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to parse baseline during conflict resolution: ${t}`,{cause:e})}}function ve(e){let t=[...e.matchAll(/<<<<<<< .*\n([\s\S]*?)\n=======\n([\s\S]*?)\n>>>>>>> .*$/gm)];if(t.length===0)throw Error(`Could not parse conflict markers in baseline`);return t.map(([,e=``,t=``])=>({ours:e,theirs:t}))}function ye(e){let t=new Map;for(let n of e)for(let[e,{items:r=[]}]of Object.entries(n.checks)){if(r.length===0)continue;let n=t.get(e);n||(n=new Map,t.set(e,n));for(let e of r)n.set(e.id,e)}let n={};for(let[e,r]of t)n[e]={items:[...r.values()].toSorted((e,t)=>e.id.localeCompare(t.id)),type:`items`};return{checks:n,version:2}}function be(e){let t=ve(e),n=[];for(let{ours:e,theirs:r}of t)n.push(z(e),z(r));return ye(n)}const B=`__unparsable__`;function V(e){return e.replaceAll(`<`,`&lt;`).replaceAll(`>`,`&gt;`).replaceAll(`[`,`&#91;`).replaceAll(`]`,`&#93;`)}function H(e,t,n){let i=r(t,e);return n?`${i}#L${n}`:i}function U(e,t){return`[${e}](${t})`}function xe(e,t){let n=H(e.file,t,e.line);return`- ${U(e.line?`Line ${e.line}`:e.file,n)} - ${`${e.rule}: ${V(e.message)}`}`}function Se(e){let t=Object.groupBy(e,e=>e.file||B);return Object.entries(t).map(([e,t=[]])=>({filePath:e,items:t})).toSorted((e,t)=>e.filePath===B?1:t.filePath===B?-1:e.filePath.localeCompare(t.filePath))}function Ce(e,t){let n=e.length,r=E(n,`issue`),i=[`\n### Other Issues · ${t}\n`];for(let t of e)i.push(`- ${t.rule}: ${V(t.message)}`);return i.push(`\n${n} ${r} in Other Issues`,``),i.join(`
12
- `)}function we(e,t,n){if(e.filePath===B)return Ce(e.items,n);let r=H(e.filePath,t),i=U(e.filePath,r),a=e.items.length,o=E(a,`issue`),s=[`\n### ${i} · ${n}\n`];for(let n of e.items)s.push(xe(n,t));return s.push(`\n${a} ${o} in ${e.filePath}`,``),s.join(`
13
- `)}function Te(e,t,n){let r=t.length,i=E(r,`issue`),a=[`\n## ${e}\n`];if(t.length===0)return a.push(`No issues`),a.join(`
14
- `);let o=Se(t);for(let t of o)a.push(we(t,n,e));return a.push(`---\n${r} total ${i} for ${e}`),a.join(`
15
- `)}function Ee(e){return`${e.replaceAll(/\n{3,}/g,`
10
+ ${F(e)}
11
+ }`}function I(e){if(typeof e!=`object`||!e)throw TypeError(`Baseline must be an object`);if(`checks`in e&&e.checks&&typeof e.checks==`object`)return{checks:e.checks,version:2};let t={};for(let[n,r]of Object.entries(e))n!==`version`&&(t[n]=r);return{checks:t,version:2}}function L(e){try{let t=P(e.trim());if(t)return I(t);let n=be(N(F(e)));return I(JSON.parse(n))}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to parse baseline during conflict resolution: ${t}`,{cause:e})}}function xe(e){let t=[...e.matchAll(/<<<<<<< .*\n([\s\S]*?)\n=======\n([\s\S]*?)\n>>>>>>> .*$/gm)];if(t.length===0)throw Error(`Could not parse conflict markers in baseline`);return t.map(([,e=``,t=``])=>({ours:e,theirs:t}))}function R(e){let t=new Map;for(let n of e)for(let[e,{items:r=[]}]of Object.entries(n.checks)){if(r.length===0)continue;let n=t.get(e);n||(n=new Map,t.set(e,n));for(let e of r)n.set(e.id,e)}let n={};for(let[e,r]of t)n[e]={items:[...r.values()].toSorted((e,t)=>e.id.localeCompare(t.id)),type:`items`};return{checks:n,version:2}}function Se(e){let t=xe(e),n=[];for(let{ours:e,theirs:r}of t)n.push(L(e),L(r));return R(n)}const z=`__unparsable__`;function B(e){return e.replaceAll(`<`,`&lt;`).replaceAll(`>`,`&gt;`).replaceAll(`[`,`&#91;`).replaceAll(`]`,`&#93;`)}function V(e,t,n){let i=r(t,e);return n?`${i}#L${n}`:i}function H(e,t){return`[${e}](${t})`}function Ce(e,t){let n=V(e.file,t,e.line);return`- ${H(e.line?`Line ${e.line}`:e.file,n)} - ${`${e.rule}: ${B(e.message)}`}`}function we(e){let t=Object.groupBy(e,e=>e.file||z);return Object.entries(t).map(([e,t=[]])=>({filePath:e,items:t})).toSorted((e,t)=>e.filePath===z?1:t.filePath===z?-1:e.filePath.localeCompare(t.filePath))}function Te(e,t){let n=e.length,r=T(n,`issue`),i=[`\n### Other Issues · ${t}\n`];for(let t of e)i.push(`- ${t.rule}: ${B(t.message)}`);return i.push(`\n${n} ${r} in Other Issues`,``),i.join(`
12
+ `)}function Ee(e,t,n){if(e.filePath===z)return Te(e.items,n);let r=V(e.filePath,t),i=H(e.filePath,r),a=e.items.length,o=T(a,`issue`),s=[`\n### ${i} · ${n}\n`];for(let n of e.items)s.push(Ce(n,t));return s.push(`\n${a} ${o} in ${e.filePath}`,``),s.join(`
13
+ `)}function De(e,t,n){let r=t.length,i=T(r,`issue`),a=[`\n## ${e}\n`];if(t.length===0)return a.push(`No issues`),a.join(`
14
+ `);let o=we(t);for(let t of o)a.push(Ee(t,n,e));return a.push(`---\n${r} total ${i} for ${e}`),a.join(`
15
+ `)}function Oe(e){return`${e.replaceAll(/\n{3,}/g,`
16
16
 
17
- `).trimEnd()}\n`}function W(e,t){let n=[`<!-- prettier-ignore-start -->
17
+ `).trimEnd()}\n`}function U(e,t){let n=[`<!-- prettier-ignore-start -->
18
18
  `,`# Mejora Baseline
19
- `,`This file represents the current accepted state of the codebase.`];for(let[r,{items:i=[]}]of Object.entries(e.checks))n.push(Te(r,i,t));return n.push(`
20
- <!-- prettier-ignore-end -->`),Ee(n.join(`
21
- `))}const De=(e,t)=>{if(!t)return!1;let n=e.items,r=t.items;if(n.length!==r.length)return!1;let i=n.toSorted((e,t)=>e.id.localeCompare(t.id)),a=r.toSorted((e,t)=>e.id.localeCompare(t.id));return i.every((e,t)=>{let n=a[t];return e.id===n.id&&e.file===n.file&&e.line===n.line&&e.column===n.column&&e.rule===n.rule&&e.message===n.message})};function Oe(e){let t=[e.message];if(e.stack){let n=e.stack.split(`
22
- `).slice(1).map(e=>x(e.trim())).join(`
19
+ `,`This file represents the current accepted state of the codebase.`];for(let[r,{items:i=[]}]of Object.entries(e.checks))n.push(De(r,i,t));return n.push(`
20
+ <!-- prettier-ignore-end -->`),Oe(n.join(`
21
+ `))}const ke=(e,t)=>{if(!t)return!1;let n=e.items,r=t.items;if(n.length!==r.length)return!1;let i=n.toSorted((e,t)=>e.id.localeCompare(t.id)),a=r.toSorted((e,t)=>e.id.localeCompare(t.id));return i.every((e,t)=>{let n=a[t];return e.id===n.id&&e.file===n.file&&e.line===n.line&&e.column===n.column&&e.rule===n.rule&&e.message===n.message})};function Ae(e){let t=[e.message];if(e.stack){let n=e.stack.split(`
22
+ `).slice(1).map(e=>b(e.trim())).join(`
23
23
  `);t.push(n)}return t.join(`
24
- `)}function G(...e){return e.map(e=>typeof e==`string`?e:e instanceof Error?Oe(e):u(e,{colors:!1,depth:10})).join(` `)}const K={error:(...e)=>{console.error(C(`✖`),G(...e))},log:(...e)=>{console.log(G(...e))},start:(...e)=>{console.log(b(`◐`),G(...e))},success:(...e)=>{console.log(S(`✔`),G(...e))}};var q=class e{baselinePath;constructor(e=`.mejora/baseline.json`){this.baselinePath=e}static create(e){return{checks:e,version:2}}static getEntry(e,t){return e?.checks[t]}static update(t,n,r){let i=t??e.create({}),a=i.checks[n];return De(r,a)?i:{...i,checks:{...i.checks,[n]:r}}}async load(){try{let e=await c(this.baselinePath,`utf8`);if(e.includes(`<<<<<<<`)){K.start(`Merge conflict detected in baseline, auto-resolving...`);let t=be(e);return await this.save(t,!0),K.success(`Baseline conflict resolved`),t}return await this.resolveMarkdownConflictIfNeeded(e),JSON.parse(e)}catch(e){if(e.code===`ENOENT`)return null;throw e}}async save(e,n=!1){if(M&&!n)return;let r=`${JSON.stringify(e,null,2)}\n`,i=this.baselinePath.replace(`.json`,`.md`),a=W(e,t(this.baselinePath));await s(t(this.baselinePath),{recursive:!0}),await Promise.all([l(this.baselinePath,r,`utf8`),l(i,a,`utf8`)])}async resolveMarkdownConflictIfNeeded(e){let n=this.baselinePath.replace(`.json`,`.md`);try{(await c(n,`utf8`)).includes(`<<<<<<<`)&&(K.start(`Merge conflict detected in markdown report, regenerating...`),await l(n,W(JSON.parse(e),t(this.baselinePath)),`utf8`),K.success(`Markdown report regenerated`))}catch{}}};function ke(){return{hasImprovement:!1,hasRegression:!1,hasRelocation:!1,isInitial:!0,newIssues:[],removedIssues:[]}}function Ae(e,t,n){return{hasImprovement:t.length>0,hasRegression:e.length>0,hasRelocation:n,isInitial:!1,newIssues:e.toSorted((e,t)=>e.id.localeCompare(t.id)),removedIssues:t.toSorted((e,t)=>e.id.localeCompare(t.id))}}function J(e=[]){return new Map(e.map(e=>[e.id,e]))}function Y(e){return new Set(e.keys())}function X(e,t){let n=[];for(let r of t){let t=e.get(r);n.push(t)}return n}function je(e,t){for(let[n,r]of e){let e=t.get(n);if(e&&(r.line!==e.line||r.column!==e.column))return!0}return!1}function Me(e,t){let n=J(e.items),r=J(t.items),i=Y(n),a=Y(r),o=i.difference(a),s=a.difference(i);return Ae(X(n,o),X(r,s),i.size>o.size?je(n,r):!1)}function Z(e,t){return t?Me(e,t):ke()}function Q(e,t){return e.file===t.file?e.line===t.line?e.column-t.column:e.line-t.line:e.file.localeCompare(t.file)}function Ne(e){let t=Map.groupBy(e,e=>e.signature),n=[];for(let[e,r]of t){r.sort(Q);for(let[t,{signature:i,...a}]of r.entries())n.push({...a,id:o(`${e}:${t}`)})}return n}function Pe(e){return{items:Ne(e.items.map(e=>({...e,signature:`${e.file} - ${e.rule}: ${e.message}`}))).toSorted(Q),type:`items`}}var Fe=class e{baselineManager;registry;constructor(e,t){this.registry=e,this.baselineManager=new q(t)}static filterChecks=(t,n)=>{let r=n.only?e.resolveRegex(n.only,`--only`):null,i=n.skip?e.resolveRegex(n.skip,`--skip`):null;return!r&&!i?t:Object.fromEntries(Object.entries(t).filter(([e])=>!(r&&!r.test(e)||i?.test(e))))};static resolveRegex(e,t){try{return new RegExp(e)}catch{throw Error(`Invalid regex pattern for ${t}: "${e}"`)}}async run(t,n={}){let r=performance.now(),i=await this.baselineManager.load(),a=e.filterChecks(t.checks,n);try{let e=m.getRequiredTypes(a);await Promise.all([this.registry.setup(e),this.registry.validate(e)])}catch(e){return K.error(`Setup failed:`,e),{exitCode:2,hasImprovement:!1,hasRegression:!0,results:[],totalDuration:performance.now()-r}}let o=Object.keys(a).length;K.start(`Running ${o} check${o===1?``:`s`}...`);let s=await this.executeChecks(a,i);if(!s)return{exitCode:2,hasImprovement:!1,hasRegression:!0,results:[],totalDuration:performance.now()-r};let c=i,l=!1,u=!1,d=!1;for(let e of s)e.hasRegression&&(l=!0),e.hasImprovement&&(u=!0),e.isInitial&&(d=!0),(e.hasImprovement||e.hasRelocation||n.force||e.isInitial)&&(c=q.update(c,e.checkId,{items:e.snapshot.items,type:e.snapshot.type}));return c&&c!==i&&(!l||n.force||d)&&await this.baselineManager.save(c,n.force),{exitCode:l&&!n.force?1:0,hasImprovement:u,hasRegression:l,results:s,totalDuration:performance.now()-r}}async executeChecks(e,t){let n=Object.entries(e).map(async([e,n])=>{try{let r=performance.now(),i=await this.registry.get(n.type).run(n),a=performance.now()-r,o=Pe(i),s=q.getEntry(t,e),c=Z(o,s);return{baseline:s,checkId:e,duration:a,hasImprovement:c.hasImprovement,hasRegression:c.hasRegression,hasRelocation:c.hasRelocation,isInitial:c.isInitial,newIssues:c.newIssues,removedIssues:c.removedIssues,snapshot:o}}catch(t){throw K.error(`Error running check "${e}":`,t),t}});try{return await Promise.all(n)}catch{return null}}};const{values:$}=d({allowPositionals:!1,options:{force:{default:!1,short:`f`,type:`boolean`},help:{short:`h`,type:`boolean`},json:{default:!1,type:`boolean`},only:{type:`string`},skip:{type:`string`}},strict:!0});$.help&&(K.log(`
24
+ `)}function W(...e){return e.map(e=>typeof e==`string`?e:e instanceof Error?Ae(e):d(e,{colors:!1,depth:10})).join(` `)}const G={error:(...e)=>{console.error(S(`✖`),W(...e))},log:(...e)=>{console.log(W(...e))},start:(...e)=>{console.log(te(`◐`),W(...e))},success:(...e)=>{console.log(x(`✔`),W(...e))}};var K=class e{baselinePath;constructor(e=`.mejora/baseline.json`){this.baselinePath=e}static create(e){return{checks:e,version:2}}static getEntry(e,t){return e?.checks[t]}static update(t,n,r){let i=t??e.create({}),a=i.checks[n];return ke(r,a)?i:{...i,checks:{...i.checks,[n]:r}}}async load(){try{let e=await l(this.baselinePath,`utf8`);if(e.includes(`<<<<<<<`)){G.start(`Merge conflict detected in baseline, auto-resolving...`);let t=Se(e);return await this.save(t,!0),G.success(`Baseline conflict resolved`),t}return await this.resolveMarkdownConflictIfNeeded(e),JSON.parse(e)}catch(e){if(e.code===`ENOENT`)return null;throw e}}async save(e,n=!1){if(A&&!n)return;let r=`${JSON.stringify(e,null,2)}\n`,i=this.baselinePath.replace(`.json`,`.md`),a=U(e,t(this.baselinePath));await c(t(this.baselinePath),{recursive:!0}),await Promise.all([u(this.baselinePath,r,`utf8`),u(i,a,`utf8`)])}async resolveMarkdownConflictIfNeeded(e){let n=this.baselinePath.replace(`.json`,`.md`);try{(await l(n,`utf8`)).includes(`<<<<<<<`)&&(G.start(`Merge conflict detected in markdown report, regenerating...`),await u(n,U(JSON.parse(e),t(this.baselinePath)),`utf8`),G.success(`Markdown report regenerated`))}catch{}}};function je(){return{hasImprovement:!1,hasRegression:!1,hasRelocation:!1,isInitial:!0,newIssues:[],removedIssues:[]}}function Me(e,t,n){return{hasImprovement:t.length>0,hasRegression:e.length>0,hasRelocation:n,isInitial:!1,newIssues:e.toSorted((e,t)=>e.id.localeCompare(t.id)),removedIssues:t.toSorted((e,t)=>e.id.localeCompare(t.id))}}function q(e=[]){return new Map(e.map(e=>[e.id,e]))}function J(e){return new Set(e.keys())}function Y(e,t){let n=[];for(let r of t){let t=e.get(r);n.push(t)}return n}function Ne(e,t){for(let[n,r]of e){let e=t.get(n);if(e&&(r.line!==e.line||r.column!==e.column))return!0}return!1}function Pe(e,t){let n=q(e.items),r=q(t.items),i=J(n),a=J(r),o=i.difference(a),s=a.difference(i);return Me(Y(n,o),Y(r,s),i.size>o.size?Ne(n,r):!1)}function X(e,t){return t?Pe(e,t):je()}function Z(e,t){return e.file===t.file?e.line===t.line?e.column-t.column:e.line-t.line:e.file.localeCompare(t.file)}function Fe(e){let t=Map.groupBy(e,e=>e.signature),n=[];for(let[e,r]of t){r.sort(Z);for(let[t,{signature:a,...o}]of r.entries())n.push({...o,id:i(`${e}:${t}`)})}return n}function Q(e){return{items:Fe(e.items.map(e=>({...e,signature:`${e.file} - ${e.rule}: ${e.message}`}))).toSorted(Z),type:`items`}}const Ie=s(new URL(`check-worker.mjs`,import.meta.url));var Le=class e{baselineManager;registry;constructor(e,t){this.registry=e,this.baselineManager=new K(t)}static async executeChecksParallel(e,t){let n=Object.keys(e),r=new m({filename:Ie});try{let e=n.map(async e=>{let n=await r.run({checkId:e}),i=Q(n.snapshot),a=K.getEntry(t,e),o=X(i,a);return{baseline:a,checkId:e,duration:n.duration,hasImprovement:o.hasImprovement,hasRegression:o.hasRegression,hasRelocation:o.hasRelocation,isInitial:o.isInitial,newIssues:o.newIssues,removedIssues:o.removedIssues,snapshot:i}});return await Promise.all(e)}catch(e){return G.error(`Parallel execution failed:`,e),null}finally{await r.destroy()}}static filterChecks=(t,n)=>{let r=n.only?e.resolveRegex(n.only,`--only`):null,i=n.skip?e.resolveRegex(n.skip,`--skip`):null;return!r&&!i?t:Object.fromEntries(Object.entries(t).filter(([e])=>!(r&&!r.test(e)||i?.test(e))))};static resolveRegex(e,t){try{return new RegExp(e)}catch{throw Error(`Invalid regex pattern for ${t}: "${e}"`)}}async run(t,n={}){let r=performance.now(),i=await this.baselineManager.load(),a=e.filterChecks(t.checks,n),o=Object.keys(a).length;G.start(`Running ${o} check${o===1?``:`s`}...`);let s=o>1?await e.executeChecksParallel(a,i):await this.executeChecksSequential(a,i);if(!s)return{exitCode:2,hasImprovement:!1,hasRegression:!0,results:[],totalDuration:performance.now()-r};let c=i,l=!1,u=!1,d=!1;for(let e of s)e.hasRegression&&(l=!0),e.hasImprovement&&(u=!0),e.isInitial&&(d=!0),(e.hasImprovement||e.hasRelocation||n.force||e.isInitial)&&(c=K.update(c,e.checkId,{items:e.snapshot.items,type:e.snapshot.type}));return c&&c!==i&&(!l||n.force||d)&&await this.baselineManager.save(c,n.force),{exitCode:l&&!n.force?1:0,hasImprovement:u,hasRegression:l,results:s,totalDuration:performance.now()-r}}async executeChecksSequential(e,t){try{let t=a.getRequiredTypes(e);await Promise.all([this.registry.setup(t),this.registry.validate(t)])}catch(e){return G.error(`Setup failed:`,e),null}let n=[];for(let[r,i]of Object.entries(e))try{let e=performance.now(),a=await this.registry.get(i.type).run(i),o=performance.now()-e,s=Q(a),c=K.getEntry(t,r),l=X(s,c);n.push({baseline:c,checkId:r,duration:o,hasImprovement:l.hasImprovement,hasRegression:l.hasRegression,hasRelocation:l.hasRelocation,isInitial:l.isInitial,newIssues:l.newIssues,removedIssues:l.removedIssues,snapshot:s})}catch(e){return G.error(`Error running check "${r}":`,e),null}return n}};const{values:$}=f({allowPositionals:!1,options:{force:{default:!1,short:`f`,type:`boolean`},help:{short:`h`,type:`boolean`},json:{default:!1,type:`boolean`},only:{type:`string`},skip:{type:`string`}},strict:!0});$.help&&(G.log(`
25
25
  mejora - Prevent regressions by allowing only improvement
26
26
 
27
27
  Usage:
@@ -40,4 +40,4 @@ Examples:
40
40
  mejora --json
41
41
  mejora --only "eslint > *"
42
42
  mejora --skip typescript
43
- `),process.exit(0));try{let e=new m;e.register(new i),e.register(new a);let t=await n();if(t.runners)for(let n of t.runners)e.register(n);let r=await new Fe(e).run(t,{force:$.force,json:$.json,only:$.only,skip:$.skip});$.json?K.log(g(r)):(K.log(``),K.log(A(r,$.force))),process.exit(r.exitCode)}catch(e){e instanceof Error?K.error(e.message):K.error(e),process.exit(2)}export{};
43
+ `),process.exit(0));try{let e=new a,t=await n();o(e,t);let r=await new Le(e).run(t,{force:$.force,json:$.json,only:$.only,skip:$.skip});$.json?G.log(ee(r)):(G.log(``),G.log(ye(r,$.force))),process.exit(r.exitCode)}catch(e){e instanceof Error?G.error(e.message):G.error(e),process.exit(2)}export{};
@@ -0,0 +1,317 @@
1
+ import { Linter } from "eslint";
2
+ import { CompilerOptions } from "typescript";
3
+
4
+ //#region src/check-runner.d.ts
5
+
6
+ /**
7
+ * Interface that all check runners must implement.
8
+ *
9
+ * Each check type (eslint, typescript, custom) implements this interface
10
+ * to provide consistent lifecycle hooks for execution.
11
+ */
12
+ interface CheckRunner<TConfig = unknown> {
13
+ /**
14
+ * Execute the check and return a snapshot of issues.
15
+ *
16
+ * @param config - Check-specific configuration
17
+ *
18
+ * @returns Snapshot containing all issues found
19
+ */
20
+ run(config: TConfig): Promise<RawSnapshot>;
21
+ /**
22
+ * Setup any infrastructure needed for this check.
23
+ * Called once during runner setup, in parallel with other checks.
24
+ *
25
+ * Examples: creating cache directories, initializing compilation state.
26
+ *
27
+ * Optional - not all checks need infrastructure setup.
28
+ */
29
+ setup?(): Promise<void>;
30
+ /**
31
+ * Unique identifier for this check type.
32
+ * Must match the `type` field in CheckConfig.
33
+ *
34
+ * @example "eslint"
35
+ *
36
+ * @example "typescript"
37
+ */
38
+ readonly type: string;
39
+ /**
40
+ * Validate that all dependencies for this check are available.
41
+ * Called once during runner setup before any checks execute.
42
+ *
43
+ * Should throw a descriptive error if dependencies are missing.
44
+ *
45
+ * @throws {Error} If required dependencies are not installed
46
+ */
47
+ validate?(): Promise<void>;
48
+ }
49
+ //#endregion
50
+ //#region src/types.d.ts
51
+ interface Issue {
52
+ /**
53
+ * 1-indexed column number for display.
54
+ */
55
+ column: number;
56
+ /**
57
+ * Relative path from cwd.
58
+ */
59
+ file: string;
60
+ /**
61
+ * Hash of canonical representation.
62
+ *
63
+ * @example "a1b2c3d4e5f6g7h8i9j0"
64
+ */
65
+ id: string;
66
+ /**
67
+ * 1-indexed line number for display.
68
+ */
69
+ line: number;
70
+ /**
71
+ * The message.
72
+ */
73
+ message: string;
74
+ /**
75
+ * Identifier for the issue (rule name, diagnostic code, etc).
76
+ *
77
+ * @example "no-nested-ternary" (ESLint)
78
+ *
79
+ * @example "TS2345" (TypeScript)
80
+ */
81
+ rule: string;
82
+ }
83
+ /**
84
+ * Issue produced by a check runner.
85
+ */
86
+ type IssueInput = Omit<Issue, "id">;
87
+ type SnapshotType = "items";
88
+ interface RawSnapshot {
89
+ /**
90
+ * Snapshot items (each item represents an issue produced by the check).
91
+ */
92
+ items: IssueInput[];
93
+ type: SnapshotType;
94
+ }
95
+ interface Snapshot {
96
+ items: Issue[];
97
+ type: SnapshotType;
98
+ }
99
+ /**
100
+ * Configuration for an ESLint check.
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * eslint({
105
+ * files: ["src/**\/*.{ts,tsx}"],
106
+ * overrides: {
107
+ * rules: {
108
+ * "no-nested-ternary": "error",
109
+ * },
110
+ * },
111
+ * })
112
+ * ```
113
+ */
114
+ interface ESLintCheckConfig {
115
+ /**
116
+ * Concurrency setting for ESLint.
117
+ *
118
+ * @see https://eslint.org/blog/2025/08/multithread-linting/#cli-multithreading-support
119
+ *
120
+ * @default "auto"
121
+ */
122
+ concurrency?: "auto" | "off" | number;
123
+ /**
124
+ * Glob patterns for files to lint.
125
+ *
126
+ * Passed directly to ESLint's `lintFiles()` method.
127
+ *
128
+ * @example ["src/**\/*.ts", "src/**\/*.tsx"]
129
+ */
130
+ files: string[];
131
+ /**
132
+ * ESLint configuration to merge with the base config.
133
+ *
134
+ * This is passed to ESLint's `overrideConfig` option and merged with
135
+ * your existing ESLint configuration.
136
+ *
137
+ * Can be a single config object or an array of config objects.
138
+ *
139
+ * @example
140
+ * ```ts
141
+ * {
142
+ * rules: {
143
+ * "no-console": "error",
144
+ * },
145
+ * }
146
+ * ```
147
+ */
148
+ overrides?: Linter.Config | Linter.Config[];
149
+ }
150
+ /**
151
+ * Configuration for a TypeScript diagnostics check.
152
+ *
153
+ * @example
154
+ * ```ts
155
+ * typescript({
156
+ * overrides: {
157
+ * compilerOptions: {
158
+ * noImplicitAny: true,
159
+ * },
160
+ * },
161
+ * })
162
+ * ```
163
+ */
164
+ interface TypeScriptCheckConfig {
165
+ /**
166
+ * Compiler options to merge with the base tsconfig.
167
+ *
168
+ * These options are merged with (not replacing) the compiler options
169
+ * from your tsconfig file.
170
+ */
171
+ overrides?: {
172
+ compilerOptions?: CompilerOptions;
173
+ };
174
+ /**
175
+ * Path to a TypeScript config file.
176
+ *
177
+ * If not provided, mejora will search for the nearest `tsconfig.json`
178
+ * starting from the current working directory.
179
+ *
180
+ * @example "tsconfig.strict.json"
181
+ */
182
+ tsconfig?: string;
183
+ }
184
+ /**
185
+ * A regex pattern configuration.
186
+ */
187
+ interface RegexPattern {
188
+ /**
189
+ * Human-readable message for matches.
190
+ *
191
+ * @example "TODO comment found"
192
+ *
193
+ * @example "console.log statement"
194
+ *
195
+ * @example (match) => `Found TODO at line ${match.index + 1}`
196
+ */
197
+ message?: ((match: RegExpExecArray) => string) | string;
198
+ /**
199
+ * The regex pattern to match.
200
+ *
201
+ * @example /\/\/\s*TODO:/gi
202
+ *
203
+ * @example /console\.log/g
204
+ */
205
+ pattern: RegExp;
206
+ /**
207
+ * Rule identifier for this pattern.
208
+ * If not provided, uses the pattern source as the rule ID.
209
+ *
210
+ * @example "no-todos"
211
+ *
212
+ * @example "no-console-log"
213
+ */
214
+ rule?: string;
215
+ }
216
+ /**
217
+ * Configuration for regex pattern matching check.
218
+ */
219
+ interface RegexCheckConfig {
220
+ /**
221
+ * Concurrency for processing files.
222
+ *
223
+ * @default 10
224
+ */
225
+ concurrency?: number;
226
+ /**
227
+ * Array of glob patterns for files to check.
228
+ *
229
+ * @example ["src/**\/*.ts", "lib/**\/*.js"]
230
+ */
231
+ files: string[];
232
+ /**
233
+ * Array of glob patterns to ignore.
234
+ *
235
+ * @default ["**\/node_modules/**", "**\/dist/**", "**\/.git/**"]
236
+ */
237
+ ignore?: string[];
238
+ /**
239
+ * Array of regex patterns to match.
240
+ */
241
+ patterns: RegexPattern[];
242
+ }
243
+ type CustomCheckConfig = Record<string, unknown> & {
244
+ type: string;
245
+ };
246
+ type CheckConfig = (ESLintCheckConfig & {
247
+ type: "eslint";
248
+ }) | (TypeScriptCheckConfig & {
249
+ type: "typescript";
250
+ }) | CustomCheckConfig;
251
+ /**
252
+ * mejora configuration.
253
+ *
254
+ * Define checks to run and track for regressions.
255
+ *
256
+ * @example
257
+ * ```ts
258
+ * import { defineConfig, eslint, typescript } from "mejora";
259
+ *
260
+ * export default defineConfig({
261
+ * checks: {
262
+ * "eslint > no-nested-ternary": eslint({
263
+ * files: ["src/**\/*.{ts,tsx}"],
264
+ * overrides: {
265
+ * rules: {
266
+ * "no-nested-ternary": "error",
267
+ * },
268
+ * },
269
+ * }),
270
+ * "typescript": typescript({
271
+ * overrides: {
272
+ * compilerOptions: {
273
+ * noImplicitAny: true,
274
+ * },
275
+ * },
276
+ * }),
277
+ * },
278
+ * });
279
+ * ```
280
+ */
281
+ interface Config {
282
+ /**
283
+ * Check definitions.
284
+ *
285
+ * Each key is a check identifier used in the baseline and output.
286
+ * The identifier can contain any characters.
287
+ *
288
+ * Use `eslint()` and `typescript()` helpers to create check configs.
289
+ *
290
+ * @example
291
+ * ```ts
292
+ * {
293
+ * "eslint > no-console": eslint({ ... }),
294
+ * "typescript": typescriptCheck({ ... }),
295
+ * }
296
+ * ```
297
+ */
298
+ checks: Record<string, CheckConfig>;
299
+ /**
300
+ * Runners to register custom check types.
301
+ *
302
+ * Built-in checks (eslint, typescript) are always available.
303
+ *
304
+ * @example
305
+ * ```ts
306
+ * {
307
+ * runners: [myCustomRunner()],
308
+ * checks: {
309
+ * "custom": myCheck({ ... })
310
+ * }
311
+ * }
312
+ * ```
313
+ */
314
+ runners?: CheckRunner[];
315
+ }
316
+ //#endregion
317
+ export { RawSnapshot as a, Snapshot as c, IssueInput as i, TypeScriptCheckConfig as l, ESLintCheckConfig as n, RegexCheckConfig as o, Issue as r, RegexPattern as s, Config as t, CheckRunner as u };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mejora",
3
- "version": "2.3.2",
3
+ "version": "2.3.3",
4
4
  "description": "Prevent regressions. Allow improvement.",
5
5
  "keywords": [
6
6
  "regression",
@@ -36,6 +36,9 @@
36
36
  "files": [
37
37
  "dist"
38
38
  ],
39
+ "dependencies": {
40
+ "tinypool": "^2.1.0"
41
+ },
39
42
  "peerDependencies": {
40
43
  "eslint": "^9.34.0",
41
44
  "tinyglobby": "^0.2.0",