@@ -55,6 +55,7 @@ import {
5555 tddStartCommand ,
5656 tddStatusCommand ,
5757 tddStopCommand ,
58+ validateTddStartOptions ,
5859} from './commands/tdd-daemon.js' ;
5960import { uploadCommand , validateUploadOptions } from './commands/upload.js' ;
6061import { validateWhoamiOptions , whoamiCommand } from './commands/whoami.js' ;
@@ -65,6 +66,7 @@ import {
6566 generateStaticReport ,
6667 getReportFileUrl ,
6768} from './services/static-report-generator.js' ;
69+ import { withTimeout } from './utils/async-utils.js' ;
6870import { openBrowser } from './utils/browser.js' ;
6971import { colors } from './utils/colors.js' ;
7072import { loadConfig } from './utils/config-loader.js' ;
@@ -376,24 +378,21 @@ let plugins = [];
376378try {
377379 plugins = await loadPlugins ( configPath , config ) ;
378380
379- for ( const plugin of plugins ) {
381+ for ( let plugin of plugins ) {
380382 try {
381383 // Add timeout protection for plugin registration (5 seconds)
382- const registerPromise = plugin . register ( program , {
384+ let registerPromise = plugin . register ( program , {
383385 config,
384386 services : pluginServices ,
385387 output,
386- // Backwards compatibility alias for plugins using old API
387- logger : output ,
388388 } ) ;
389- const timeoutPromise = new Promise ( ( _ , reject ) =>
390- setTimeout (
391- ( ) => reject ( new Error ( 'Plugin registration timeout (5s)' ) ) ,
392- 5000
393- )
389+
390+ await withTimeout (
391+ registerPromise ,
392+ 5000 ,
393+ 'Plugin registration timeout (5s)'
394394 ) ;
395395
396- await Promise . race ( [ registerPromise , timeoutPromise ] ) ;
397396 output . debug ( `Registered plugin: ${ plugin . name } ` ) ;
398397 } catch ( error ) {
399398 output . warn ( `Failed to register plugin ${ plugin . name } : ${ error . message } ` ) ;
@@ -418,15 +417,13 @@ program
418417 . argument ( '<path>' , 'Path to screenshots directory or file' )
419418 . option ( '-b, --build-name <name>' , 'Build name for grouping' )
420419 . option ( '-m, --metadata <json>' , 'Additional metadata as JSON' )
421- . option ( '--batch-size <n>' , 'Upload batch size' , v => parseInt ( v , 10 ) )
422- . option ( '--upload-timeout <ms>' , 'Upload timeout in milliseconds' , v =>
423- parseInt ( v , 10 )
424- )
420+ . option ( '--batch-size <n>' , 'Upload batch size' , Number )
421+ . option ( '--upload-timeout <ms>' , 'Upload timeout in milliseconds' , Number )
425422 . option ( '--branch <branch>' , 'Git branch' )
426423 . option ( '--commit <sha>' , 'Git commit SHA' )
427424 . option ( '--message <msg>' , 'Commit message' )
428425 . option ( '--environment <env>' , 'Environment name' , 'test' )
429- . option ( '--threshold <number>' , 'Comparison threshold' , parseFloat )
426+ . option ( '--threshold <number>' , 'Comparison threshold' , Number )
430427 . option ( '--token <token>' , 'API token override' )
431428 . option ( '--wait' , 'Wait for build completion' )
432429 . option ( '--upload-all' , 'Upload all screenshots without SHA deduplication' )
@@ -461,7 +458,12 @@ tddCmd
461458 . option ( '--baseline-build <id>' , 'Use specific build as baseline' )
462459 . option ( '--baseline-comparison <id>' , 'Use specific comparison as baseline' )
463460 . option ( '--environment <env>' , 'Environment name' , 'test' )
464- . option ( '--threshold <number>' , 'Comparison threshold' , parseFloat )
461+ . option ( '--threshold <number>' , 'Comparison threshold' , Number )
462+ . option (
463+ '--min-cluster-size <pixels>' ,
464+ 'Minimum changed-pixel cluster size' ,
465+ Number
466+ )
465467 . option ( '--timeout <ms>' , 'Server timeout in milliseconds' , '30000' )
466468 . option ( '--fail-on-diff' , 'Fail tests when visual differences are detected' )
467469 . option ( '--token <token>' , 'API token override' )
@@ -475,6 +477,15 @@ tddCmd
475477 return ;
476478 }
477479
480+ let validationErrors = validateTddStartOptions ( options ) ;
481+ if ( validationErrors . length > 0 ) {
482+ output . error ( 'Validation errors:' ) ;
483+ for ( let error of validationErrors ) {
484+ output . printErr ( ` - ${ error } ` ) ;
485+ }
486+ process . exit ( 1 ) ;
487+ }
488+
478489 await tddStartCommand ( options , globalOptions ) ;
479490 } ) ;
480491
@@ -512,7 +523,12 @@ tddCmd
512523 . option ( '--port <port>' , 'Port for TDD server' , '47392' )
513524 . option ( '--branch <branch>' , 'Git branch override' )
514525 . option ( '--environment <env>' , 'Environment name' , 'test' )
515- . option ( '--threshold <number>' , 'Comparison threshold' , parseFloat )
526+ . option ( '--threshold <number>' , 'Comparison threshold' , Number )
527+ . option (
528+ '--min-cluster-size <pixels>' ,
529+ 'Minimum changed-pixel cluster size' ,
530+ Number
531+ )
516532 . option ( '--token <token>' , 'API token override' )
517533 . option ( '--timeout <ms>' , 'Server timeout in milliseconds' , '30000' )
518534 . option ( '--baseline-build <id>' , 'Use specific build as baseline' )
@@ -602,6 +618,14 @@ program
602618 . option ( '--commit <sha>' , 'Git commit SHA' )
603619 . option ( '--message <msg>' , 'Commit message' )
604620 . option ( '--environment <env>' , 'Environment name' , 'test' )
621+ . option ( '--threshold <number>' , 'Comparison threshold' , Number )
622+ . option (
623+ '--min-cluster-size <pixels>' ,
624+ 'Minimum changed-pixel cluster size' ,
625+ Number
626+ )
627+ . option ( '--batch-size <n>' , 'Upload batch size' , Number )
628+ . option ( '--upload-timeout <ms>' , 'Upload timeout in milliseconds' , Number )
605629 . option ( '--token <token>' , 'API token override' )
606630 . option ( '--wait' , 'Wait for build completion' )
607631 . option ( '--timeout <ms>' , 'Server timeout in milliseconds' , '30000' )
@@ -664,13 +688,8 @@ program
664688 . option ( '--environment <env>' , 'Filter by environment' )
665689 . option ( '-p, --project <slug>' , 'Filter by project slug' )
666690 . option ( '--org <slug>' , 'Filter by organization slug' )
667- . option (
668- '--limit <n>' ,
669- 'Maximum results to return (1-250)' ,
670- val => parseInt ( val , 10 ) ,
671- 20
672- )
673- . option ( '--offset <n>' , 'Skip first N results' , val => parseInt ( val , 10 ) , 0 )
691+ . option ( '--limit <n>' , 'Maximum results to return (1-250)' , Number , 20 )
692+ . option ( '--offset <n>' , 'Skip first N results' , Number , 0 )
674693 . option ( '--comparisons' , 'Include comparisons when fetching a specific build' )
675694 . addHelpText (
676695 'after' ,
@@ -710,13 +729,8 @@ program
710729 . option ( '--name <pattern>' , 'Search comparisons by name (supports wildcards)' )
711730 . option ( '--status <status>' , 'Filter by status (identical, new, changed)' )
712731 . option ( '--branch <branch>' , 'Filter by branch (for name search)' )
713- . option (
714- '--limit <n>' ,
715- 'Maximum results to return (1-250)' ,
716- val => parseInt ( val , 10 ) ,
717- 50
718- )
719- . option ( '--offset <n>' , 'Skip first N results' , val => parseInt ( val , 10 ) , 0 )
732+ . option ( '--limit <n>' , 'Maximum results to return (1-250)' , Number , 50 )
733+ . option ( '--offset <n>' , 'Skip first N results' , Number , 0 )
720734 . option ( '-p, --project <slug>' , 'Filter by project slug' )
721735 . option ( '--org <slug>' , 'Filter by organization slug' )
722736 . addHelpText (
@@ -789,17 +803,17 @@ contextCmd
789803 . option (
790804 '--similar-limit <n>' ,
791805 'Maximum similar fingerprint matches to return (1-50)' ,
792- val => parseInt ( val , 10 )
806+ Number
793807 )
794808 . option (
795809 '--recent-limit <n>' ,
796810 'Maximum recent same-name comparisons to return (1-50)' ,
797- val => parseInt ( val , 10 )
811+ Number
798812 )
799813 . option (
800814 '--window-size <n>' ,
801815 'Historical hotspot analysis window size (1-50)' ,
802- val => parseInt ( val , 10 )
816+ Number
803817 )
804818 . addHelpText (
805819 'after' ,
@@ -835,12 +849,12 @@ contextCmd
835849 . option (
836850 '--recent-limit <n>' ,
837851 'Maximum recent comparisons to return (1-50)' ,
838- val => parseInt ( val , 10 )
852+ Number
839853 )
840854 . option (
841855 '--window-size <n>' ,
842856 'Historical hotspot analysis window size (1-50)' ,
843- val => parseInt ( val , 10 )
857+ Number
844858 )
845859 . addHelpText (
846860 'after' ,
@@ -873,9 +887,7 @@ contextCmd
873887 . option ( '--source <source>' , 'Context source: auto, cloud, or local' , 'auto' )
874888 . option ( '-p, --project <slug-or-id>' , 'Project scope for user auth lookups' )
875889 . option ( '--org <slug>' , 'Organization slug when project slug is ambiguous' )
876- . option ( '--limit <n>' , 'Maximum matches to return (1-50)' , val =>
877- parseInt ( val , 10 )
878- )
890+ . option ( '--limit <n>' , 'Maximum matches to return (1-50)' , Number )
879891 . addHelpText (
880892 'after' ,
881893 `
@@ -905,10 +917,8 @@ contextCmd
905917 . option ( '--source <source>' , 'Context source: auto, cloud, or local' , 'auto' )
906918 . option ( '-p, --project <slug-or-id>' , 'Project scope for user auth lookups' )
907919 . option ( '--org <slug>' , 'Organization slug when project slug is ambiguous' )
908- . option ( '--limit <n>' , 'Maximum comparisons to return (1-100)' , val =>
909- parseInt ( val , 10 )
910- )
911- . option ( '--offset <n>' , 'Skip first N comparisons' , val => parseInt ( val , 10 ) )
920+ . option ( '--limit <n>' , 'Maximum comparisons to return (1-100)' , Number )
921+ . option ( '--offset <n>' , 'Skip first N comparisons' , Number )
912922 . addHelpText (
913923 'after' ,
914924 `
@@ -1179,13 +1189,8 @@ program
11791189 . command ( 'projects' )
11801190 . description ( 'List projects you have access to' )
11811191 . option ( '--org <slug>' , 'Filter by organization slug' )
1182- . option (
1183- '--limit <n>' ,
1184- 'Maximum results to return (1-250)' ,
1185- val => parseInt ( val , 10 ) ,
1186- 50
1187- )
1188- . option ( '--offset <n>' , 'Skip first N results' , val => parseInt ( val , 10 ) , 0 )
1192+ . option ( '--limit <n>' , 'Maximum results to return (1-250)' , Number , 50 )
1193+ . option ( '--offset <n>' , 'Skip first N results' , Number , 0 )
11891194 . addHelpText (
11901195 'after' ,
11911196 `
@@ -1240,7 +1245,6 @@ program
12401245 . description ( 'Upload static files as a preview for a build' )
12411246 . argument ( '[path]' , 'Path to static files (dist/, build/, out/)' )
12421247 . option ( '-b, --build <id>' , 'Build ID to attach preview to' )
1243- . option ( '-p, --parallel-id <id>' , 'Look up build by parallel ID' )
12441248 . option ( '--base <path>' , 'Override auto-detected base path' )
12451249 . option ( '--open' , 'Open preview URL in browser after upload' )
12461250 . option ( '--dry-run' , 'Show what would be uploaded without uploading' )
0 commit comments