import { basename , join } from 'path' ; import { promises as fs } from 'fs' ; import { exec } from 'child_process' ; import { homedir , tmpdir } from 'os' ; export { watchFile } from './fs-watch' ; interface Task < T > { done : ( value : T ) => void ; promise : Promise < T > ; } type TypeChecker = ( thing : any ) => boolean ; interface Types { string : TypeChecker ; number : TypeChecker ; array : TypeChecker ; object : TypeChecker ; null : TypeChecker ; asyncfunction : TypeChecker ; function : TypeChecker ; promise : TypeChecker ; map : TypeChecker ; set : TypeChecker ; } export const parseGuifont = ( guifont ) => { const [ font ] = guifont . match ( /(?:\\,|[^,])+/g ) || [ '' ] ; const [ face , ... settings ] = font . split ( ':' ) ; const height = settings . find ( ( s ) => s . startsWith ( 'h' ) ) ; const size = Math . round ( < any > ( height || '' ) . slice ( 1 ) - 0 ) ; return { face , size , } ; } ; export const $HOME = homedir ? homedir ( ) : 'Why are you using this from the frontend? Stop it.' ; const type = ( m ) => ( Object . prototype . toString . call ( m ) . match ( /^\[object (\w+)\]/ ) || [ ] ) [ 1 ] . toLowerCase ( ) ; export const within = ( target , tolerance ) => ( candidate ) => Math . abs ( target - candidate ) <= tolerance ; export const merge = Object . assign ; export const $ = < T > ( ... fns ) => ( ... a ) => fns . reduce ( ( res , fn , ix ) => ( ix ? fn ( res ) : fn ( ... res ) ) , a ) as unknown as T ; export const is = new Proxy < Types > ( { } as Types , { get : ( _ , key ) => ( val ) => type ( val ) === key , } ) ; export const onFnCall = < T > ( cb ) : T => new Proxy ( { } , { get : ( _ , name ) => ( ... args ) => cb ( name as string , args ) , } ) as T ; export const proxyFn = ( cb ) => new Proxy ( { } , { get : ( _ , name ) => ( data ? ) => cb ( name as string , data ) } ) as { [ index ] : ( data ? : any ) => void ; } ; export const CreateTask = < T > ( ) : Task < T > => ( ( done = ( _ ) => { } , promise = new Promise < T > ( ( m ) => ( done = m ) ) ) => ( { done , promise , } ) ) ( ) ; export const uuid = ( ) : string => ( < any > [ 1e7 ] + - 1e3 + - 4e3 + - 8e3 + - 1e11 ) . replace ( /[018]/g , ( a ) => ( a ^ ( ( Math . random ( ) * 16 ) >> ( a / 4 ) ) ) . toString ( 16 ) ) ; export const shell = ( cmd , opts ? ) : Promise < string > => new Promise ( ( fin ) => exec ( cmd , opts , ( _ , out ) => fin ( out + '' ) ) ) ; export const getPipeName = ( name ) => join ( tmpdir ( ) , `${ name }${ uuid ( ) }.sock` ) ; export const arrReplace = < T > ( arr , matcher , patch ) : T [ ] | undefined => { const foundIndex = arr . findIndex ( matcher ) ; if ( ! ~ foundIndex ) return ; const copy = [ ... arr ] ; const itemToPatch = copy [ foundIndex ] ; const patchedItem = { ... itemToPatch , ... patch } ; copy . splice ( foundIndex , 1 , patchedItem ) ; return copy ; } ; export const minmax = ( min , max ) => ( ... numbers ) => { return Math . min ( max , Math . max ( min , ... numbers ) ) ; } ; export const pathRelativeTo = ( path , otherPath ) => path . includes ( otherPath ) ? path . replace ( otherPath , '' ) . replace ( /^\// , '' ) : path ; export const simplifyPath = ( fullpath , cwd , homeDirIfUsingFromWeb ? ) => fullpath . includes ( cwd ) ? fullpath . split ( cwd + '/' ) [ 1 ] : fullpath . includes ( homeDirIfUsingFromWeb ?? $HOME ) ? fullpath . replace ( homeDirIfUsingFromWeb ?? $HOME , '~' ) : fullpath ; export const pathReducer = ( p = '' ) => ( ( p , levels = 0 ) => ( { reduce : ( ) => levels ? basename ( join ( p , '../' . repeat ( levels ++ ) ) ) : ( levels ++ , basename ( p ) ) , } ) ) ( p ) ; export const asColor = ( color ? ) => color ? '#' + [ 16 , 8 , 0 ] . map ( ( shift ) => { const mask = 0xff << shift ; const hex = ( ( color & mask ) >> shift ) . toString ( 16 ) ; return hex . length < 2 ? '0' + hex : hex ; } ) . join ( '' ) : undefined ; export const exists = async ( path ) : Promise < boolean > => fs . access ( path ) . then ( ( ) => true ) . catch ( ( ) => false ) ; const emptyStat = { isDirectory : ( ) => false , isFile : ( ) => false , isSymbolicLink : ( ) => false , } ; const getFSStat = async ( path ) => fs . stat ( path ) . catch ( ( _ ) => emptyStat ) ; interface DirFileInfo { name : string ; path : string ; relativePath : string ; dir : boolean ; file : boolean ; symlink : boolean ; } export const getDirFiles = async ( path ) : Promise < DirFileInfo [ ] > => { const paths = ( await fs . readdir ( path ) . catch ( ( _e ) => [ ] ) ) as string [ ] ; const filepaths = paths . map ( ( f ) => ( { name : f , path : join ( path , f ) } ) ) ; const filesreq = await Promise . all ( filepaths . map ( async ( f ) => ( { path : f . path , name : f . name , relativePath : f . path , stats : await getFSStat ( f . path ) , } ) ) ) ; return filesreq . map ( ( { name , path , relativePath , stats } ) => ( { name , path , relativePath , dir : stats . isDirectory ( ) , file : stats . isFile ( ) || path . endsWith ( '.asar' ) , symlink : stats . isSymbolicLink ( ) , } ) ) ; } ; export const getDirs = async ( path ) => ( await getDirFiles ( path ) ) . filter ( ( m ) => m . dir ) ; export function debounce ( fn , wait = 1 ) { if ( ! fn ) throw new Error ( 'bruh, ya need a function here!' ) ; let timeout ; return function ( this , ... args ) { const ctx = this ; clearTimeout ( timeout ) ; timeout = setTimeout ( ( ) => fn . apply ( ctx , args ) , wait ) ; } ; } export const throttle = ( fn , delay ) => { let throttling = false ; let args ; const executor = ( ... a ) => { if ( throttling ) return ( args = a ) , undefined ; throttling = true ; fn ( ... a ) ; setTimeout ( ( ) => ( ( throttling = false ) , args && ( executor ( ... args ) , ( args = undefined ) ) ) , delay ) ; } ; return executor ; } ; class MapSetter < A , B > extends Map < A , Set < B > > { add ( key , value ) { const s = this . get ( key ) || new Set ( ) ; this . set ( key , s . add ( value ) ) ; return ( ) => this . remove ( key , value ) ; } addMany ( key , values ) { return values . map ( ( val ) => this . add ( key , val ) ) ; } addMultiple ( keys , value ) { keys . forEach ( ( key ) => this . add ( key , value ) ) ; return ( ) => this . removeMultiple ( keys , value ) ; } addMultipleValues ( keys , values ) { const removalFuncs = values . map ( ( value ) => this . addMultiple ( keys , value ) ) ; return ( ) => removalFuncs . forEach ( ( fn ) => fn ( ) ) ; } replace ( key , value ) { const s = this . get ( key ) || new Set ( ) ; s . clear ( ) ; this . set ( key , s . add ( value ) ) ; return ( ) => this . remove ( key , value ) ; } replaceMany ( key , values ) { const s = this . get ( key ) || new Set ( ) ; s . clear ( ) ; return values . map ( ( val ) => this . add ( key , val ) ) ; } remove ( key , value ) { const s = this . get ( key ) ; if ( ! s ) return false ; return s . delete ( value ) ; } removeMultiple ( keys , value ) { keys . forEach ( ( key ) => this . remove ( key , value ) ) ; } getList ( key ) { const s = this . get ( key ) ; return s ? [ ... s ] : [ ] ; } } export const MapSet = < A , B , C > ( initial ? ) => { const m = new Map < A , MapSetter < B , C > > ( initial ) ; const set = ( key , subkey , value ) => { const sub = m . get ( key ) || new MapSetter ( ) ; sub . add ( subkey , value ) ; m . set ( key , sub ) ; } ; const get = ( key , subkey ) => { const sub = m . get ( key ) ; if ( ! sub ) return ; return sub . get ( subkey ) ; } ; const has = ( key , subkey ) => { const sub = m . get ( key ) ; if ( ! sub ) return false ; return sub . has ( subkey ) ; } ; const remove = ( key , subkey ) => { const sub = m . get ( key ) ; if ( ! sub ) return ; sub . delete ( subkey ) ; } ; const forEach = ( key , fn ) => { const sub = m . get ( key ) ; if ( ! sub ) return ; sub . forEach ( fn ) ; } ; const size = ( ) => m . size ; const subsize = ( key ) => { const sub = m . get ( key ) ; if ( ! sub ) return - 1 ; return sub . size ; } ; const keys = ( key ) => { const sub = m . get ( key ) ; if ( ! sub ) return [ ] ; return sub . keys ( ) ; } ; const entries = ( key ) => { const sub = m . get ( key ) ; if ( ! sub ) return [ ] ; return [ ... sub . entries ( ) ] ; } ; return { get raw ( ) { return m ; } , set , get , has , remove , forEach , size , subsize , keys , entries , } ; } ;