@@ -617,7 +617,9 @@ export class RegistryClient {
617617 const metadata = await this . getComposerPackageMetadata ( pkg . name )
618618
619619 // Find multiple update paths: patch, minor, and major
620- const currentVersion = pkg . version
620+ // Extract the base version from the constraint (e.g., "^3.0" -> "3.0.0")
621+ const constraintBaseVersion = this . extractConstraintBaseVersion ( constraint )
622+ const currentVersion = constraintBaseVersion || pkg . version
621623 const latestVersion = pkg . latest
622624
623625 // Get all available versions by querying composer show
@@ -633,8 +635,8 @@ export class RegistryClient {
633635 availableVersions = [ latestVersion ]
634636 }
635637
636- // Find the best update for each type (patch, minor, major )
637- const updateCandidates = await this . findBestUpdates ( currentVersion , availableVersions , constraint )
638+ // Find the best constraint updates (e.g., ^3.0 -> ^3.9.0 )
639+ const updateCandidates = await this . findBestConstraintUpdates ( constraint , availableVersions , currentVersion )
638640
639641 for ( const candidate of updateCandidates ) {
640642 const updateType = getUpdateType ( currentVersion , candidate . version )
@@ -917,4 +919,81 @@ export class RegistryClient {
917919
918920 return undefined
919921 }
922+
923+ /**
924+ * Extract the base version from a version constraint (e.g., "^3.0" -> "3.0.0")
925+ */
926+ private extractConstraintBaseVersion ( constraint : string ) : string | null {
927+ const match = constraint . match ( / ^ [ \^ ~ > = < ] * ( [ \d . ] + ) / )
928+ if ( match ) {
929+ return match [ 1 ]
930+ }
931+ return null
932+ }
933+
934+ /**
935+ * Find the best constraint updates (e.g., ^3.0 -> ^3.9.0)
936+ */
937+ private async findBestConstraintUpdates ( constraint : string , availableVersions : string [ ] , currentVersion : string ) : Promise < { version : string , type : 'patch' | 'minor' | 'major' } [ ] > {
938+ const { getUpdateType } = await import ( '../utils/helpers' )
939+ const candidates : { version : string , type : 'patch' | 'minor' | 'major' } [ ] = [ ]
940+
941+ // Parse current version
942+ const currentParts = this . parseVersion ( currentVersion )
943+ if ( ! currentParts ) {
944+ return [ ]
945+ }
946+
947+ let bestPatch : string | null = null
948+ let bestMinor : string | null = null
949+ let bestMajor : string | null = null
950+
951+ for ( const version of availableVersions ) {
952+ // Skip dev/alpha/beta versions for now (could be enhanced later)
953+ if ( version . includes ( 'dev' ) || version . includes ( 'alpha' ) || version . includes ( 'beta' ) || version . includes ( 'RC' ) ) {
954+ continue
955+ }
956+
957+ const versionParts = this . parseVersion ( version )
958+ if ( ! versionParts ) {
959+ continue
960+ }
961+
962+ // Skip versions that are not newer
963+ const comparison = this . compareVersions ( version , currentVersion )
964+ if ( comparison <= 0 ) {
965+ continue
966+ }
967+
968+ const updateType = getUpdateType ( currentVersion , version )
969+
970+ // Find best update for each type
971+ if ( updateType === 'patch' && versionParts . major === currentParts . major && versionParts . minor === currentParts . minor ) {
972+ if ( ! bestPatch || this . compareVersions ( version , bestPatch ) > 0 ) {
973+ bestPatch = version
974+ }
975+ } else if ( updateType === 'minor' && versionParts . major === currentParts . major ) {
976+ if ( ! bestMinor || this . compareVersions ( version , bestMinor ) > 0 ) {
977+ bestMinor = version
978+ }
979+ } else if ( updateType === 'major' ) {
980+ if ( ! bestMajor || this . compareVersions ( version , bestMajor ) > 0 ) {
981+ bestMajor = version
982+ }
983+ }
984+ }
985+
986+ // Add the best candidates
987+ if ( bestPatch ) {
988+ candidates . push ( { version : bestPatch , type : 'patch' } )
989+ }
990+ if ( bestMinor ) {
991+ candidates . push ( { version : bestMinor , type : 'minor' } )
992+ }
993+ if ( bestMajor ) {
994+ candidates . push ( { version : bestMajor , type : 'major' } )
995+ }
996+
997+ return candidates
998+ }
920999}
0 commit comments