@@ -135,15 +135,68 @@ export default class PluginValidate extends Command {
135135 const engine = ( manifest as any ) . manifest ?. engine || ( manifest as any ) . engine ;
136136 const required = engine ?. objectstack as string | undefined ;
137137 if ( required ) {
138- // Simple semver range check (>=X.Y.Z)
139- const rangeMatch = required . match ( / > = ? \s * ( [ \d . ] + ) / ) ;
140- if ( rangeMatch ) {
141- const [ rMajor , rMinor = '0' , rPatch = '0' ] = rangeMatch [ 1 ] . split ( '.' ) ;
142- const [ pMajor , pMinor = '0' , pPatch = '0' ] = flags . platformVersion . split ( '.' ) ;
143- const compatible =
144- parseInt ( pMajor ) > parseInt ( rMajor ) ||
145- ( parseInt ( pMajor ) === parseInt ( rMajor ) && parseInt ( pMinor ) > parseInt ( rMinor ) ) ||
146- ( parseInt ( pMajor ) === parseInt ( rMajor ) && parseInt ( pMinor ) === parseInt ( rMinor ) && parseInt ( pPatch ) >= parseInt ( rPatch ) ) ;
138+ // Semver range check supporting >=, >, ^, ~, and exact versions
139+ const parseSemver = ( v : string ) => {
140+ const parts = v . replace ( / ^ v / , '' ) . split ( '.' ) . map ( p => parseInt ( p , 10 ) ) ;
141+ return { major : parts [ 0 ] || 0 , minor : parts [ 1 ] || 0 , patch : parts [ 2 ] || 0 } ;
142+ } ;
143+
144+ const target = parseSemver ( flags . platformVersion ) ;
145+ let compatible = false ;
146+ let matched = false ;
147+
148+ // >=X.Y.Z — greater than or equal
149+ const gteMatch = required . match ( / ^ > = \s * ( [ \d . ] + ) / ) ;
150+ if ( gteMatch ) {
151+ const req = parseSemver ( gteMatch [ 1 ] ) ;
152+ compatible = ( target . major > req . major ) ||
153+ ( target . major === req . major && target . minor > req . minor ) ||
154+ ( target . major === req . major && target . minor === req . minor && target . patch >= req . patch ) ;
155+ matched = true ;
156+ }
157+
158+ // >X.Y.Z — strictly greater than
159+ if ( ! matched ) {
160+ const gtMatch = required . match ( / ^ > \s * ( [ \d . ] + ) / ) ;
161+ if ( gtMatch ) {
162+ const req = parseSemver ( gtMatch [ 1 ] ) ;
163+ compatible = ( target . major > req . major ) ||
164+ ( target . major === req . major && target . minor > req . minor ) ||
165+ ( target . major === req . major && target . minor === req . minor && target . patch > req . patch ) ;
166+ matched = true ;
167+ }
168+ }
169+
170+ // ^X.Y.Z — caret range (same major, >= minor.patch)
171+ if ( ! matched ) {
172+ const caretMatch = required . match ( / ^ \^ \s * ( [ \d . ] + ) / ) ;
173+ if ( caretMatch ) {
174+ const req = parseSemver ( caretMatch [ 1 ] ) ;
175+ compatible = target . major === req . major &&
176+ ( ( target . minor > req . minor ) ||
177+ ( target . minor === req . minor && target . patch >= req . patch ) ) ;
178+ matched = true ;
179+ }
180+ }
181+
182+ // ~X.Y.Z — tilde range (same major.minor, >= patch)
183+ if ( ! matched ) {
184+ const tildeMatch = required . match ( / ^ ~ \s * ( [ \d . ] + ) / ) ;
185+ if ( tildeMatch ) {
186+ const req = parseSemver ( tildeMatch [ 1 ] ) ;
187+ compatible = target . major === req . major && target . minor === req . minor && target . patch >= req . patch ;
188+ matched = true ;
189+ }
190+ }
191+
192+ // Exact version match
193+ if ( ! matched && / ^ \d + \. \d + \. \d + $ / . test ( required ) ) {
194+ const req = parseSemver ( required ) ;
195+ compatible = target . major === req . major && target . minor === req . minor && target . patch === req . patch ;
196+ matched = true ;
197+ }
198+
199+ if ( matched ) {
147200 platformResult = { compatible, requiredRange : required , targetVersion : flags . platformVersion } ;
148201 findings . push ( {
149202 severity : compatible ? 'info' : 'error' ,
0 commit comments