@@ -739,42 +739,147 @@ async function availability(clusterName, options) {
739739 } ", ${ availabilityStrings . join ( ', ' ) } )`;
740740}
741741
742- function wasIntroducedBeforeRelease ( releaseName , clusterName , options ) {
743- const data = fetchAvailabilityData ( this . global ) ;
744- const path = makeAvailabilityPath ( clusterName , options ) ;
742+ /**
743+ * Utility for wasIntroducedBeforeRelease and isSupported. Returns undefined if
744+ * the the path we are looking at was not officially introduced, otherwise
745+ * returns -1 if it was introduced before the reference release, 0 if it was
746+ * introduced in the reference release, 1 if it was introduced after the
747+ * reference release.
748+ *
749+ * Throws if referenceRelease is not defined.
750+ */
751+ function compareIntroductionToReferenceRelease ( global , path , options , referenceRelease ) {
752+ if ( referenceRelease === undefined ) {
753+ throw new Error ( "Can't compare to non-existent release" ) ;
754+ }
755+
756+ const data = fetchAvailabilityData ( global ) ;
745757
746758 let introducedRelease = findReleaseForPath (
747759 data ,
748760 [ 'introduced' , ...path ] ,
749761 options
750762 ) ;
751763 if ( introducedRelease === undefined ) {
752- return false ;
764+ return undefined ;
765+ }
766+
767+ let referenceIndex = data . indexOf ( referenceRelease ) ;
768+ let introducedIndex = data . indexOf ( introducedRelease ) ;
769+ if ( introducedIndex < referenceIndex ) {
770+ return - 1 ;
771+ }
772+
773+ if ( introducedIndex > referenceIndex ) {
774+ return 1 ;
753775 }
754776
777+ return 0 ;
778+ }
779+
780+ function wasIntroducedBeforeRelease ( releaseName , clusterName , options ) {
781+ const data = fetchAvailabilityData ( this . global ) ;
782+ const path = makeAvailabilityPath ( clusterName , options ) ;
783+
755784 let referenceRelease = findReleaseByName ( data , releaseName ) ;
756785 if ( referenceRelease === undefined ) {
757786 throw new Error ( `Invalid release name: ${ releaseName } ` ) ;
758787 }
759788
760- return data . indexOf ( introducedRelease ) < data . indexOf ( referenceRelease ) ;
789+ let comparisonStatus = compareIntroductionToReferenceRelease (
790+ this . global ,
791+ makeAvailabilityPath ( clusterName , options ) ,
792+ options , referenceRelease
793+ ) ;
794+ if ( comparisonStatus === undefined ) {
795+ // Not introduced yet, so not introduced before anything in particular.
796+ return false ;
797+ }
798+
799+ return comparisonStatus == - 1 ;
761800}
762801
763- function wasRemoved ( cluster , options ) {
764- const data = fetchAvailabilityData ( this . global ) ;
765- const path = makeAvailabilityPath ( cluster , options ) ;
802+ /**
803+ * Utility for wasRemoved and findProvisionalRelease. Finds a release that
804+ * mentions the given path or some ancestor of it in the given section. Returns
805+ * the release and the path that ended up being found, or undefined if nothing
806+ * was found.
807+ */
808+ function findReleaseForPathOrAncestorAndSection ( global , cluster , options , section ) {
809+ const data = fetchAvailabilityData ( global ) ;
810+ let path = makeAvailabilityPath ( cluster , options ) ;
766811
767- let removedRelease = undefined ;
768- let removalPath = [ ...path ] ;
769- while ( removedRelease === undefined && removalPath !== undefined ) {
770- removedRelease = findReleaseForPath (
812+ while ( path !== undefined ) {
813+ let foundRelease = findReleaseForPath (
771814 data ,
772- [ 'removed' , ...removalPath ] ,
815+ [ section , ...path ] ,
773816 options
774817 ) ;
775- removalPath = findPathToContainer ( removalPath ) ;
818+ if ( foundRelease !== undefined ) {
819+ return { release : foundRelease , path : path } ;
820+ }
821+ path = findPathToContainer ( path ) ;
822+ }
823+ return undefined ;
824+ }
825+
826+ function wasRemoved ( cluster , options ) {
827+ return findReleaseForPathOrAncestorAndSection ( this . global , cluster , options , 'removed' ) !== undefined ;
828+ }
829+
830+ function pathsEqual ( path1 , path2 ) {
831+ if ( path1 . length != path2 . length ) {
832+ return false ;
833+ }
834+
835+ for ( let i = 0 ; i < path1 . length ; ++ i ) {
836+ if ( path1 [ i ] != path2 [ i ] ) {
837+ return false ;
838+ }
839+ }
840+
841+ return true ;
842+ }
843+
844+ function isSupported ( cluster , options ) {
845+ if ( wasRemoved . call ( this , cluster , options ) ) {
846+ return false ;
847+ }
848+
849+ let provisionalRelease = findReleaseForPathOrAncestorAndSection ( this . global , cluster , options , 'provisional' ) ;
850+ if ( provisionalRelease === undefined ) {
851+ // Default to enabled, even if not explicitly introduced.
852+ return true ;
853+ }
854+
855+ let path = makeAvailabilityPath ( cluster , options ) ;
856+ while ( path !== undefined ) {
857+ let comparisonStatus = compareIntroductionToReferenceRelease (
858+ this . global ,
859+ path ,
860+ options ,
861+ provisionalRelease . release
862+ ) ;
863+
864+ // If we have an explicit introduction for something that is at the scope of
865+ // the provisional thing or narrower, and that introduction comes no earlier
866+ // than the provisional marking (we allow the same release for cases we
867+ // unfortunately have where we introduced some parts of a provisional
868+ // thing), then this is supported.
869+ if ( comparisonStatus === 1 || comparisonStatus === 0 ) {
870+ return true ;
871+ }
872+
873+ // If we have walked all the way up to the path to the provisional thing
874+ // without finding an overriding introduction, we are done.
875+ if ( pathsEqual ( path , provisionalRelease . path ) ) {
876+ break ;
877+ }
878+
879+ path = findPathToContainer ( path ) ;
776880 }
777- return removedRelease !== undefined ;
881+
882+ return false ;
778883}
779884
780885function hasRenamedFields ( cluster , options ) {
@@ -921,6 +1026,7 @@ exports.compatCommandNameRemapping = compatCommandNameRemapping;
9211026exports . availability = availability ;
9221027exports . wasIntroducedBeforeRelease = wasIntroducedBeforeRelease ;
9231028exports . wasRemoved = wasRemoved ;
1029+ exports . isSupported = isSupported ;
9241030exports . and = and ;
9251031exports . or = or ;
9261032exports . not = not ;
0 commit comments