88 *
99 * @example
1010 * # Full usage with reports
11- * node scripts/aggregate-outcomes.js \
11+ * node --experimental-strip-types scripts/aggregate-outcomes.ts \
1212 * --outcomes-dir ./outcomes \
1313 * --reports-dir ./reports \
1414 * --previous-reports-dir ./previous-reports \
1717 *
1818 * @example
1919 * # Using short flags
20- * node scripts/aggregate-outcomes.js \
20+ * node --experimental-strip-types scripts/aggregate-outcomes.ts \
2121 * -o ./outcomes \
2222 * -r ./reports \
2323 * -p ./previous-reports \
2626 *
2727 * @example
2828 * # Minimal usage (no trends)
29- * node scripts/aggregate-outcomes.js -o ./outcomes -r ./reports
29+ * node --experimental-strip-types scripts/aggregate-outcomes.ts -o ./outcomes -r ./reports
3030 */
3131
3232import fs from "node:fs" ;
@@ -35,40 +35,81 @@ import { parseArgs } from "node:util";
3535
3636/**
3737 * Minimal outcome data from matrix jobs
38- *
39- * @typedef {Object } MinimalOutcome
40- * @property {string } id - Project identifier (e.g., "ant-design")
41- * @property {string } outcome - Test outcome: "success" | "failure" | "skipped" | other
4238 */
39+ export interface MinimalOutcome {
40+ /** Project identifier (e.g., "ant-design") */
41+ id: string ;
42+ /** Test outcome: "success" | "failure" | "skipped" | other */
43+ outcome: string ;
44+ }
45+
46+ /**
47+ * Duration object from Biome JSON report
48+ */
49+ export interface Duration {
50+ /** Seconds */
51+ secs: number ;
52+ /** Nanoseconds */
53+ nanos: number ;
54+ }
55+
56+ /**
57+ * Summary statistics from Biome JSON report
58+ */
59+ export interface BiomeSummary {
60+ /** Execution duration */
61+ duration ? : Duration ;
62+ /** Number of errors */
63+ errors ? : number ;
64+ /** Number of warnings */
65+ warnings ? : number ;
66+ /** Number of info diagnostics */
67+ infos ? : number ;
68+ }
4369
4470/**
4571 * Biome JSON report structure
46- *
47- * @typedef {Object } BiomeReport
48- * @property {Object } summary - Summary statistics
49- * @property {Object } summary.duration - Execution duration
50- * @property {number } summary.duration.secs - Seconds
51- * @property {number } summary.duration.nanos - Nanoseconds
52- * @property {number } summary.errors - Number of errors
53- * @property {number } summary.warnings - Number of warnings
54- * @property {number } summary.infos - Number of info diagnostics
5572 */
73+ export interface BiomeReport {
74+ /** Summary statistics */
75+ summary ? : BiomeSummary ;
76+ /** Error flag (set when report generation failed) */
77+ error ? : boolean ;
78+ }
5679
5780/**
5881 * Full outcome data with computed trends
59- *
60- * @typedef {Object } OutcomeData
61- * @property {string } id - Project identifier (e.g., "ant-design")
62- * @property {string } tag - Status emoji with indicators:
63- * - "✅" = success
64- * - "❌" = failure
65- * - "❓" = skipped/timeout/other
66- * - "🆕" = new project (appended to base emoji)
67- * - "📈" = more diagnostics than before (appended)
68- * - "📉" = fewer diagnostics than before (appended)
69- * @property {string } time - Execution time (e.g., "1.2s", "30ms", "?")
70- * @property {string } outcome - Test outcome: "success" | "failure" | "skipped" | other
7182 */
83+ export interface OutcomeData {
84+ /** Project identifier (e.g., "ant-design") */
85+ id: string ;
86+ /**
87+ * Status emoji with indicators:
88+ * - "✅" = success
89+ * - "❌" = failure
90+ * - "❓" = skipped/timeout/other
91+ * - "🆕" = new project (appended to base emoji)
92+ * - "📈" = more diagnostics than before (appended)
93+ * - "📉" = fewer diagnostics than before (appended)
94+ * - "⚠️" = error running biome (appended)
95+ */
96+ tag: string ;
97+ /** Execution time (e.g., "1.2s", "30ms", "?") */
98+ time: string ;
99+ /** Test outcome: "success" | "failure" | "skipped" | other */
100+ outcome: string ;
101+ }
102+
103+ /**
104+ * Configuration from CLI arguments
105+ */
106+ export interface Config {
107+ outcomesDir : string ;
108+ reportsDir: string ;
109+ previousReportsDir ? : string ;
110+ biomeRef ? : string ;
111+ runUrl ? : string ;
112+ }
72113
73114/*
74115 * Example JSON data structure that the script expects to receive:
@@ -109,9 +150,9 @@ import { parseArgs } from "node:util";
109150/**
110151 * Display usage information
111152 */
112- function showHelp ( ) {
153+ export function showHelp ( ) : void {
113154 console . info ( `
114- Usage: aggregate-outcomes.js [options]
155+ Usage: aggregate-outcomes.ts [options]
115156
116157Aggregates Biome ecosystem CI outcome data, computes trends from JSON reports,
117158and generates a Discord message.
@@ -126,18 +167,18 @@ Options:
126167
127168Examples:
128169 # Full usage with trend computation
129- node scripts/aggregate-outcomes.js \\
170+ node --experimental-strip-types scripts/aggregate-outcomes.ts \\
130171 --outcomes-dir ./outcomes \\
131172 --reports-dir ./reports \\
132173 --previous-reports-dir ./previous-reports \\
133174 --biome-ref main \\
134175 --run-url "https://github.com/biomejs/ecosystem-ci/actions/runs/123456"
135176
136177 # Using short flags
137- node scripts/aggregate-outcomes.js -o ./outcomes -r ./reports -p ./prev -b main -u "https://..."
178+ node --experimental-strip-types scripts/aggregate-outcomes.ts -o ./outcomes -r ./reports -p ./prev -b main -u "https://..."
138179
139180 # Without trends (no previous reports)
140- node scripts/aggregate-outcomes.js -o ./outcomes -r ./reports
181+ node --experimental-strip-types scripts/aggregate-outcomes.ts -o ./outcomes -r ./reports
141182
142183Expected Directory Structure:
143184 outcomes/
@@ -161,9 +202,8 @@ Output:
161202
162203/**
163204 * Parse command line arguments using Node.js util.parseArgs
164- * @returns {{outcomesDir: string, reportsDir: string, previousReportsDir?: string, biomeRef?: string, runUrl?: string} }
165205 */
166- function parseCliArgs ( ) {
206+ export function parseCliArgs ( ) : Config {
167207 const { values } = parseArgs ( {
168208 options : {
169209 "outcomes-dir" : {
@@ -221,11 +261,9 @@ function parseCliArgs() {
221261
222262/**
223263 * Read all minimal outcome JSON files from the outcomes directory
224- * @param {string } outcomesDir - Path to directory containing outcome-* subdirectories
225- * @returns {MinimalOutcome[] }
226264 */
227- function readMinimalOutcomes ( outcomesDir ) {
228- const outcomes = [ ] ;
265+ export function readMinimalOutcomes ( outcomesDir : string ) : MinimalOutcome [ ] {
266+ const outcomes : MinimalOutcome [ ] = [ ] ;
229267
230268 try {
231269 const entries = fs . readdirSync ( outcomesDir ) ;
@@ -237,12 +275,15 @@ function readMinimalOutcomes(outcomesDir) {
237275
238276 if ( fs . existsSync ( outcomeFile ) ) {
239277 const content = fs . readFileSync ( outcomeFile , "utf-8" ) ;
240- const data = JSON . parse ( content ) ;
278+ const data = JSON . parse ( content ) as MinimalOutcome ;
241279 outcomes . push ( data ) ;
242280 }
243281 }
244282 } catch ( error ) {
245- console . error ( `Error reading outcomes from ${ outcomesDir } :` , error . message ) ;
283+ console . error (
284+ `Error reading outcomes from ${ outcomesDir } :` ,
285+ ( error as Error ) . message ,
286+ ) ;
246287 process . exit ( 1 ) ;
247288 }
248289
@@ -251,11 +292,11 @@ function readMinimalOutcomes(outcomesDir) {
251292
252293/**
253294 * Read a biome JSON report file
254- * @param {string } reportsDir - Directory containing biome-report-* subdirectories
255- * @param {string } projectId - Project identifier
256- * @returns {BiomeReport | null } - Parsed report or null if not found
257295 */
258- function readReport ( reportsDir , projectId ) {
296+ export function readReport (
297+ reportsDir : string ,
298+ projectId : string ,
299+ ) : BiomeReport | null {
259300 const reportPath = path . join (
260301 reportsDir ,
261302 `biome-report-${ projectId } ` ,
@@ -265,7 +306,7 @@ function readReport(reportsDir, projectId) {
265306 if ( fs . existsSync ( reportPath ) ) {
266307 try {
267308 const content = fs . readFileSync ( reportPath , "utf-8" ) ;
268- const report = JSON . parse ( content ) ;
309+ const report = JSON . parse ( content ) as BiomeReport ;
269310
270311 // Check if this is an error placeholder
271312 if ( report . error === true ) {
@@ -275,12 +316,9 @@ function readReport(reportsDir, projectId) {
275316 return report ;
276317 } catch ( error ) {
277318 console . error (
278- `Warning: Failed to parse JSON report for ${ projectId } : ${ error . message } ` ,
319+ `Warning: Failed to parse JSON report for ${ projectId } : ${ ( error as Error ) . message } ` ,
279320 ) ;
280321 console . error ( `Report path: ${ reportPath } ` ) ;
281- console . error (
282- `Content preview: ${ content . substring ( 0 , 200 ) } ...` ,
283- ) ;
284322 return null ;
285323 }
286324 }
@@ -290,10 +328,8 @@ function readReport(reportsDir, projectId) {
290328
291329/**
292330 * Format duration from biome report
293- * @param {Object } duration - Duration object with secs and nanos
294- * @returns {string } - Formatted duration (e.g., "1.2s", "234ms")
295331 */
296- function formatDuration ( duration ) {
332+ export function formatDuration ( duration : Duration | undefined ) : string {
297333 if ( ! duration ) return "?" ;
298334
299335 const totalMs = duration . secs * 1000 + duration . nanos / 1000000 ;
@@ -307,10 +343,8 @@ function formatDuration(duration) {
307343
308344/**
309345 * Get total diagnostic count from report
310- * @param {BiomeReport } report - Biome report
311- * @returns {number } - Total number of diagnostics
312346 */
313- function getTotalDiagnostics ( report ) {
347+ export function getTotalDiagnostics ( report : BiomeReport | null ) : number {
314348 if ( ! report || ! report . summary ) return 0 ;
315349 return (
316350 ( report . summary . errors || 0 ) +
@@ -321,11 +355,11 @@ function getTotalDiagnostics(report) {
321355
322356/**
323357 * Compare two reports and determine trend indicator
324- * @param {BiomeReport | null } previousReport - Previous report
325- * @param {BiomeReport | null } currentReport - Current report
326- * @returns {string } - Trend indicator: "🆕" | "📈" | "📉" | "⚠️" | ""
327358 */
328- function computeTrend ( previousReport , currentReport ) {
359+ export function computeTrend (
360+ previousReport : BiomeReport | null ,
361+ currentReport : BiomeReport | null ,
362+ ) : string {
329363 // Error in current run
330364 if ( currentReport ?. error ) {
331365 return "⚠️" ;
@@ -354,10 +388,8 @@ function computeTrend(previousReport, currentReport) {
354388
355389/**
356390 * Compute base tag from test outcome
357- * @param {string } outcome - Test outcome: "success" | "failure" | other
358- * @returns {string } - Base tag emoji
359391 */
360- function computeBaseTag ( outcome ) {
392+ export function computeBaseTag ( outcome : string ) : string {
361393 if ( outcome === "success" ) {
362394 return "✅" ;
363395 }
@@ -369,16 +401,12 @@ function computeBaseTag(outcome) {
369401
370402/**
371403 * Compute full outcome data with trends
372- * @param {MinimalOutcome } minimalOutcome - Minimal outcome from matrix job
373- * @param {string } reportsDir - Directory with current reports
374- * @param {string | undefined } previousReportsDir - Directory with previous reports (optional)
375- * @returns {OutcomeData } - Full outcome with computed tag, time, and trends
376404 */
377- function computeFullOutcome (
378- minimalOutcome ,
379- reportsDir ,
380- previousReportsDir ,
381- ) {
405+ export function computeFullOutcome (
406+ minimalOutcome : MinimalOutcome ,
407+ reportsDir : string ,
408+ previousReportsDir ?: string ,
409+ ) : OutcomeData {
382410 const { id, outcome } = minimalOutcome ;
383411
384412 // Read reports
@@ -409,12 +437,12 @@ function computeFullOutcome(
409437
410438/**
411439 * Aggregate outcomes and generate Discord message
412- * @param {OutcomeData[] } outcomes - Array of outcome data
413- * @param {string | undefined } biomeRef - Biome reference (branch/tag), optional
414- * @param {string | undefined } runUrl - GitHub Actions run URL, optional
415- * @returns {string } - Formatted Discord message
416440 */
417- function aggregateResults ( outcomes , biomeRef , runUrl ) {
441+ export function aggregateResults (
442+ outcomes : OutcomeData [ ] ,
443+ biomeRef ? : string ,
444+ runUrl ? : string ,
445+ ) : string {
418446 // Sort outcomes by id for consistent ordering
419447 outcomes . sort ( ( a , b ) => a . id . localeCompare ( b . id ) ) ;
420448
@@ -439,7 +467,7 @@ function aggregateResults(outcomes, biomeRef, runUrl) {
439467 const total = outcomes . length ;
440468
441469 // Build the message parts
442- const messageParts = [ ] ;
470+ const messageParts : string [ ] = [ ] ;
443471
444472 // Add header with optional biome ref
445473 if ( biomeRef ) {
@@ -468,7 +496,7 @@ function aggregateResults(outcomes, biomeRef, runUrl) {
468496/**
469497 * Main function
470498 */
471- function main ( ) {
499+ export function main ( ) : void {
472500 const config = parseCliArgs ( ) ;
473501
474502 // Read minimal outcomes
@@ -496,5 +524,3 @@ function main() {
496524 ) ;
497525 console . info ( message ) ;
498526}
499-
500- main ( ) ;
0 commit comments