import { checksum } from "@xmcl/core" ; import { open } from "@xmcl/unzip" ; import { createReadStream } from "fs" ; export interface Validator { param fd param destination param url validate ( fd , destination , url ) : Promise < void > ; } export class ChecksumValidator implements Validator { constructor ( protected checksum ? ) { } async validate ( fd , destination , url ) { if ( this . checksum ) { const actual = await checksum ( destination , this . checksum . algorithm ) ; const expect = this . checksum . hash ; if ( actual !== expect ) { throw new ChecksumNotMatchError ( this . checksum . algorithm , this . checksum . hash , actual , destination , url ) ; } } } } export function isValidator ( options ? ) { if ( ! options ) { return false ; } return "validate" in options && typeof options . validate === "function" ; } export function resolveValidator ( options ? ) { if ( isValidator ( options ) ) { return options ; } if ( options ) { return new ChecksumValidator ( { hash : options . hash , algorithm : options . algorithm } ) ; } return { validate ( ) { return Promise . resolve ( ) ; } } ; } export interface ChecksumValidatorOptions { algorithm : string ; hash : string ; } export class ZipValidator implements Validator { async validate ( fd , destination , url ) { try { const file = await open ( fd ) ; file . close ( ) ; } catch ( e ) { throw new ValidationError ( "InvalidZipError" , ( e as any ) . message ) ; } } } export class JsonValidator implements Validator { validate ( fd , destination , url ) { return new Promise < void > ( ( resolve , reject ) => { const read = createReadStream ( destination , { fd , autoClose : false , emitClose : true , } ) ; let content = "" ; read . on ( "data" , ( buf ) => { content += buf . toString ( ) ; } ) ; read . on ( "end" , ( ) => { try { JSON . parse ( content ) ; resolve ( ) ; } catch ( e ) { reject ( e ) ; } } ) ; } ) ; } } export class ValidationError extends Error { constructor ( readonly error , message ? ) { super ( message ) ; } } export class ChecksumNotMatchError extends ValidationError { constructor ( readonly algorithm , readonly expect , readonly actual , readonly file , readonly source ? ) { super ( "ChecksumNotMatchError" , source ? `File ${ file } (${ source }) ${ algorithm } checksum not match. Expect: ${ expect }. Actual: ${ actual }.` : `File ${ file } ${ algorithm } checksum not match. Expect: ${ expect }. Actual: ${ actual }.` ) ; } }