/********************************************************************************
 * Copyright (C) 2021 TypeFox and others.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the Eclipse
 * Public License v. 2.0 are satisfied: GNU General Public License, version 2
 * with the GNU Classpath Exception which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 ********************************************************************************/ import * as bent from 'bent' ; import * as semver from 'semver' ; import { VSXAllVersions , VSXBuiltinNamespaces , VSXExtensionRaw , VSXQueryParam , VSXQueryResult , VSXSearchEntry , VSXSearchParam , VSXSearchResult } from './ovsx-types' ; const fetchText = bent ( 'GET' , 'string' , 200 ) ; const fetchJson = bent ( 'GET' , { 'Accept' : 'application/json' } , 'json' , 200 ) ; export interface OVSXClientOptions { apiVersion : string ; apiUrl : string ; } export class OVSXClient { constructor ( readonly options ) { } async search ( param ? ) { const searchUri = await this . buildSearchUri ( param ) ; return this . fetchJson < VSXSearchResult > ( searchUri ) ; } protected async buildSearchUri ( param ? ) { return this . buildUri ( 'api/-/search' , param ) ; } protected buildQueryUri ( param ? ) { return this . buildUri ( 'api/-/query' , param ) ; } protected buildUri ( url , param ? ) { let searchUri = '' ; if ( param ) { const query = [ ] ; for ( const [ key , value ] of Object . entries ( param ) ) { if ( typeof value === 'string' ) { query . push ( `${ key }=${ encodeURIComponent ( value ) }` ) ; } else if ( typeof value === 'boolean' || typeof value === 'number' ) { query . push ( `${ key }=${ String ( value ) }` ) ; } } if ( query . length > 0 ) { searchUri += '?' + query . join ( '&' ) ; } } return new URL ( `${ url }${ searchUri }` , this . options ! . apiUrl ) . toString ( ) ; } async getExtension ( id ) { const param = { extensionId : id } ; const apiUri = this . buildQueryUri ( param ) ; const result = await this . fetchJson < VSXQueryResult > ( apiUri ) ; if ( result . extensions && result . extensions . length > 0 ) { return result . extensions [ 0 ] ; } throw new Error ( `Extension with id ${ id } not found at ${ apiUri }` ) ; } param id async getAllVersions ( id ) { const param = { extensionId : id , includeAllVersions : true , } ; const apiUri = this . buildQueryUri ( param ) ; const result = await this . fetchJson < VSXQueryResult > ( apiUri ) ; if ( result . extensions && result . extensions . length > 0 ) { return result . extensions ; } throw new Error ( `Extension with id ${ id } not found at ${ apiUri }` ) ; } protected fetchJson < R > ( url ) { return fetchJson ( url ) as Promise < R > ; } fetchText ( url ) { return fetchText ( url ) ; } param id returns async getLatestCompatibleExtensionVersion ( id ) { const extensions = await this . getAllVersions ( id ) ; if ( extensions . length === 0 ) { return undefined ; } const namespace = extensions [ 0 ] . namespace . toLowerCase ( ) ; if ( this . isBuiltinNamespace ( namespace ) ) { const apiVersion = this . options ! . apiVersion ; for ( const extension of extensions ) { if ( this . isVersionLTE ( extension . version , apiVersion ) ) { return extension ; } } console . log ( `Skipping: built-in extension "${ id }" at version "${ apiVersion }" does not exist.` ) ; } else { for ( const extension of extensions ) { if ( this . isEngineSupported ( extension . engines ?. vscode ) ) { return extension ; } } } } param entry returns getLatestCompatibleVersion ( entry ) { const extensions = entry . allVersions ; if ( this . isBuiltinNamespace ( entry . namespace ) ) { const apiVersion = this . options ! . apiVersion ; for ( const extension of extensions ) { if ( this . isVersionLTE ( extension . version , apiVersion ) ) { return extension ; } } } else { for ( const extension of extensions ) { if ( this . isEngineSupported ( extension . engines ?. vscode ) ) { return extension ; } } } } param engine returns protected isEngineSupported ( engine ? ) { if ( ! engine ) { return false ; } if ( engine === '*' ) { return true ; } else { return semver . satisfies ( this . options ! . apiVersion , engine ) ; } } param namespace protected isBuiltinNamespace ( namespace ) { return namespace === VSXBuiltinNamespaces . VSCODE || namespace === VSXBuiltinNamespaces . THEIA ; } param a param b protected isVersionLTE ( a , b ) { const versionA = semver . clean ( a ) ; const versionB = semver . clean ( b ) ; if ( ! versionA || ! versionB ) { return false ; } return semver . lte ( versionA , versionB ) ; } }