import JSZip from 'jszip' ; import { StatusCode , getResponseError } from '~/services/http' ; import { Native , BuildType } from '../types' ; import type { Binding , BindingDefinition , BindingMapSelection , BuildStore , PlatformSelection } from '../types' ; type AddonSelection = Array < { id : string ; version : string ; } > ; interface SelectedBuildConfig { path : string ; selected : Array < Binding > ; build : BuildType ; platforms : PlatformSelection ; source : boolean ; javadoc : boolean ; includeJSON : boolean ; version : string ; addons : AddonSelection ; } export function getBuild ( state ) { let path ; let _build ; const platformCount = state . natives . allIds . length ; const selectedPlatforms = state . platform ; if ( state . build === BuildType . Release ) { path = `release/${ state . version }` ; _build = BuildType . Release ; } else if ( state . build !== null ) { _build = state . build ; path = state . build ; } else { throw new Error ( 'no build selected' ) ; } const selected = state . artifacts . allIds . filter ( ( artifact ) => { if ( ! state . contents [ artifact ] ) { return false ; } const spec = state . artifacts . byId [ artifact ] as BindingDefinition ; return ( spec . natives === undefined || spec . nativesOptional === true || spec . natives . length === platformCount || spec . natives . some ( ( platform ) => selectedPlatforms [ platform ] ) ) ; } ) ; const addons = state . selectedAddons . map ( ( addon ) => ( { id : addon , version : state . addons . byId [ addon ] . maven . version , } ) ) ; return { path , selected , build : _build , platforms : selectedPlatforms , source : state . source , javadoc : state . javadoc , includeJSON : state . includeJSON , version : state . version , addons , } ; } export const fetchManifest = async ( path ) : Promise < Array < string > > => { const response = await fetch ( `/bin/${ path }` , { method : 'GET' , mode : 'same-origin' , credentials : 'omit' , headers : { Accept : 'application/json' , } , } ) ; if ( response . status !== StatusCode . OK ) { throw new Error ( await getResponseError ( response ) ) ; } return await response . json ( ) ; } ; export function getFiles ( path , manifest , selected , platforms , source , javadoc ) { const files = [ ] ; const rootRegExp = new RegExp ( `^${ path }/bin/` ) ; const javaDocRegExp = /-javadoc\.jar$/ ; const sourcesRegExp = /-sources\.jar$/ ; const nativeRegExp = /-natives-/ ; const platformRegExp = /-natives-(\w+(?:-\w+)*)\.jar$/ ; const selectedMap = { } as BindingMapSelection ; selected . forEach ( ( key ) => { selectedMap [ key ] = true ; } ) ; manifest . forEach ( ( file ) => { const filepath = file . replace ( rootRegExp , '' ) ; if ( filepath . endsWith ( '/' ) || ! filepath . length ) { return ; } if ( filepath . indexOf ( '/' ) === - 1 ) { files . push ( filepath ) ; return ; } const folder = filepath . split ( '/' ) [ 0 ] ; if ( ! ( folder in selectedMap ) ) { return ; } if ( javaDocRegExp . test ( filepath ) && javadoc === false ) { return ; } else if ( sourcesRegExp . test ( filepath ) && source === false ) { return ; } else if ( nativeRegExp . test ( filepath ) ) { const platform = filepath . match ( platformRegExp ) ; if ( platform === null ) { return ; } const native = pathToNative ( platform [ 1 ] ) ; if ( native === null || platforms [ native ] !== true ) { return ; } } files . push ( filepath ) ; } ) ; return files . map ( ( filepath ) : string => `${ path }/bin/${ filepath }` ) ; } function pathToNative ( folder ) { switch ( folder ) { case 'linux' : return Native . Linux ; case 'linux-arm64' : return Native . LinuxARM64 ; case 'linux-arm32' : return Native . LinuxARM32 ; case 'macos' : return Native . MacOS ; case 'macos-arm64' : return Native . MacOSARM64 ; case 'windows' : return Native . Windows ; case 'windows-x86' : return Native . WindowsX86 ; case 'windows-arm64' : return Native . WindowsARM64 ; } return null ; } export function getAddons ( addons , source , javadoc ) { const files = [ ] ; addons . forEach ( ( addon ) => { const { id , version } = addon ; files . push ( `addons/${ id }/${ id }-${ version }.jar` ) ; files . push ( `addons/${ id }/${ id }_license.txt` ) ; if ( javadoc ) { files . push ( `addons/${ id }/${ id }-${ version }-javadoc.jar` ) ; } if ( source ) { files . push ( `addons/${ id }/${ id }-${ version }-sources.jar` ) ; } } ) ; return files ; } async function fetchFile ( path , abortSignal ? ) { const fetchOptions = { method : 'GET' , mode : 'cors' , credentials : 'omit' , } ; if ( abortSignal !== undefined ) { fetchOptions . signal = abortSignal ; } let response = await fetch ( `https://build.lwjgl.org/${ path }` , fetchOptions ) ; if ( response . status !== StatusCode . OK ) { throw new Error ( await getResponseError ( response ) ) ; } return { payload : await response . blob ( ) , filename : path . split ( '/' ) . pop ( ) as string , } ; } let abortController = null ; let abortSignal = false ; export function abortDownload ( ) { abortSignal = true ; if ( abortController !== null ) { abortController . abort ( ) ; } } export function downloadFiles ( files , jszip , log ) { const FILES_TOTAL = files . length ; const PARALLEL_DOWNLOADS = Math . min ( 6 , FILES_TOTAL ) ; const queue = files [ Symbol . iterator ] ( ) ; const channels = [ ] ; let f = 0 ; abortSignal = false ; if ( typeof AbortController !== 'undefined' ) { abortController = new AbortController ( ) ; } for ( let i = 0 ; i < PARALLEL_DOWNLOADS ; i += 1 ) { channels . push ( new Promise ( async ( resolve , reject ) => { for ( let path of queue ) { f += 1 ; log ( `${ f }/${ FILES_TOTAL } - ${ path }` ) ; try { if ( abortSignal ) { const err = new Error ( ) ; err . name = 'AbortError' ; throw err ; } const download = await fetchFile ( path , abortController !== null ? abortController . signal : undefined ) ; jszip . file ( download . filename , download . payload , { binary : true } ) ; } catch ( err ) { reject ( err ) ; return ; } } resolve ( ) ; } ) ) ; } return Promise . all ( channels ) ; }