Updated logger & emitter
This commit is contained in:
		@@ -1,6 +1,6 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	"name": "@ztimson/js-utilities",
 | 
						"name": "@ztimson/js-utilities",
 | 
				
			||||||
	"version": "0.3.8",
 | 
						"version": "0.4.0",
 | 
				
			||||||
	"description": "JavaScript Utility library",
 | 
						"description": "JavaScript Utility library",
 | 
				
			||||||
	"author": "Zak Timson",
 | 
						"author": "Zak Timson",
 | 
				
			||||||
	"license": "MIT",
 | 
						"license": "MIT",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,23 +42,23 @@ export const CliBackground = {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export enum LOG_LEVEL {
 | 
					export enum LOG_LEVEL {
 | 
				
			||||||
	VERBOSE,
 | 
						ERROR = 0,
 | 
				
			||||||
	DEBUG = 0,
 | 
						WARN = 1,
 | 
				
			||||||
	LOG = 1,
 | 
					 | 
				
			||||||
	INFO = 2,
 | 
						INFO = 2,
 | 
				
			||||||
	WARN = 3,
 | 
						LOG = 3,
 | 
				
			||||||
	ERROR = 4,
 | 
						DEBUG = 4,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type LoggerEvents = TypedEvents & {
 | 
					export type LoggerEvents = TypedEvents & {
 | 
				
			||||||
	'VERBOSE': (...args: any[]) => any;
 | 
					 | 
				
			||||||
	'INFO': (...args: any[]) => any;
 | 
					 | 
				
			||||||
	'WARN': (...args: any[]) => any;
 | 
					 | 
				
			||||||
	'ERROR': (...args: any[]) => any;
 | 
						'ERROR': (...args: any[]) => any;
 | 
				
			||||||
 | 
						'WARN': (...args: any[]) => any;
 | 
				
			||||||
 | 
						'INFO': (...args: any[]) => any;
 | 
				
			||||||
 | 
						'LOG': (...args: any[]) => any;
 | 
				
			||||||
 | 
						'DEBUG': (...args: any[]) => any;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Logger extends TypedEmitter<LoggerEvents> {
 | 
					export class Logger extends TypedEmitter<LoggerEvents> {
 | 
				
			||||||
	static LOG_LEVEL: LOG_LEVEL = LOG_LEVEL.VERBOSE;
 | 
						static LOG_LEVEL: LOG_LEVEL = LOG_LEVEL.DEBUG;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	constructor(public readonly namespace?: string) {
 | 
						constructor(public readonly namespace?: string) {
 | 
				
			||||||
		super();
 | 
							super();
 | 
				
			||||||
@@ -79,35 +79,35 @@ export class Logger extends TypedEmitter<LoggerEvents> {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	debug(...args: string[]) {
 | 
						debug(...args: string[]) {
 | 
				
			||||||
		if(Logger.LOG_LEVEL > LOG_LEVEL.VERBOSE) return;
 | 
							if(Logger.LOG_LEVEL < LOG_LEVEL.DEBUG) return;
 | 
				
			||||||
		const str = this.format(...args);
 | 
							const str = this.format(...args);
 | 
				
			||||||
		Logger.emit(LOG_LEVEL.VERBOSE, str);
 | 
							Logger.emit(LOG_LEVEL.DEBUG, str);
 | 
				
			||||||
		console.debug(CliForeground.LIGHT_GREY + str + CliEffects.CLEAR);
 | 
							console.debug(CliForeground.LIGHT_GREY + str + CliEffects.CLEAR);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log(...args: string[]) {
 | 
						log(...args: string[]) {
 | 
				
			||||||
		if(Logger.LOG_LEVEL > LOG_LEVEL.INFO) return;
 | 
							if(Logger.LOG_LEVEL < LOG_LEVEL.LOG) return;
 | 
				
			||||||
		const str = this.format(...args);
 | 
							const str = this.format(...args);
 | 
				
			||||||
		Logger.emit(LOG_LEVEL.INFO, str);
 | 
							Logger.emit(LOG_LEVEL.LOG, str);
 | 
				
			||||||
		console.log(CliEffects.CLEAR + str);
 | 
							console.log(CliEffects.CLEAR + str);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	info(...args: string[]) {
 | 
						info(...args: string[]) {
 | 
				
			||||||
		if(Logger.LOG_LEVEL > LOG_LEVEL.INFO) return;
 | 
							if(Logger.LOG_LEVEL < LOG_LEVEL.INFO) return;
 | 
				
			||||||
		const str = this.format(...args);
 | 
							const str = this.format(...args);
 | 
				
			||||||
		Logger.emit(LOG_LEVEL.INFO, str);
 | 
							Logger.emit(LOG_LEVEL.INFO, str);
 | 
				
			||||||
		console.info(CliForeground.BLUE + str + CliEffects.CLEAR);
 | 
							console.info(CliForeground.BLUE + str + CliEffects.CLEAR);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	warn(...args: string[]) {
 | 
						warn(...args: string[]) {
 | 
				
			||||||
		if(Logger.LOG_LEVEL > LOG_LEVEL.WARN) return;
 | 
							if(Logger.LOG_LEVEL < LOG_LEVEL.WARN) return;
 | 
				
			||||||
		const str = this.format(...args);
 | 
							const str = this.format(...args);
 | 
				
			||||||
		Logger.emit(LOG_LEVEL.WARN, str);
 | 
							Logger.emit(LOG_LEVEL.WARN, str);
 | 
				
			||||||
		console.warn(CliForeground.YELLOW + str + CliEffects.CLEAR);
 | 
							console.warn(CliForeground.YELLOW + str + CliEffects.CLEAR);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	error(...args: string[]) {
 | 
						error(...args: string[]) {
 | 
				
			||||||
		if(Logger.LOG_LEVEL > LOG_LEVEL.ERROR) return;
 | 
							if(Logger.LOG_LEVEL < LOG_LEVEL.ERROR) return;
 | 
				
			||||||
		const str = this.format(...args);
 | 
							const str = this.format(...args);
 | 
				
			||||||
		Logger.emit(LOG_LEVEL.ERROR, str);
 | 
							Logger.emit(LOG_LEVEL.ERROR, str);
 | 
				
			||||||
		console.error(CliForeground.RED + str + CliEffects.CLEAR);
 | 
							console.error(CliForeground.RED + str + CliEffects.CLEAR);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										138
									
								
								src/xhr.ts
									
									
									
									
									
								
							
							
						
						
									
										138
									
								
								src/xhr.ts
									
									
									
									
									
								
							@@ -1,92 +1,88 @@
 | 
				
			|||||||
export type FetchInterceptor = (resp: Response, next: () => any) => any;
 | 
					import {TypedEmitter, type TypedEvents} from './emitter';
 | 
				
			||||||
 | 
					import {clean} from './objects';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class XHR<T> {
 | 
					export type Interceptor = (request: Response, next: () => void) => void;
 | 
				
			||||||
	private static interceptors: {[key: number]: FetchInterceptor} = {};
 | 
					 | 
				
			||||||
	static headers: Record<string, string | null> = {};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private interceptors: {[key: string]: FetchInterceptor} = {};
 | 
					export type RequestOptions = {
 | 
				
			||||||
 | 
						url?: string;
 | 
				
			||||||
 | 
						method?: 'GET' | 'POST' | 'PATCH' | 'DELETE';
 | 
				
			||||||
 | 
						body?: any;
 | 
				
			||||||
 | 
						headers?: {[key: string | symbol]: string | null | undefined};
 | 
				
			||||||
 | 
						[key: string]: any;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	constructor(public readonly baseUrl: string,
 | 
					export type XhrEvents = TypedEvents & {
 | 
				
			||||||
				public readonly headers: Record<string, string | null> = {}
 | 
						'REQUEST': (request: Promise<any>, options: RequestOptions) => any;
 | 
				
			||||||
	) { }
 | 
						'RESPONSE': (response: Response, options: RequestOptions) => any;
 | 
				
			||||||
 | 
						'REJECTED': (response: Error, options: RequestOptions) => any;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	static addInterceptor(fn: FetchInterceptor): () => {};
 | 
					};
 | 
				
			||||||
	static addInterceptor(key: string, fn: FetchInterceptor): () => {};
 | 
					
 | 
				
			||||||
	static addInterceptor(keyOrFn: string | FetchInterceptor, fn?: FetchInterceptor): () => {} {
 | 
					export type XhrOptions = {
 | 
				
			||||||
		const func: any = fn ? fn : keyOrFn;
 | 
						interceptors?: Interceptor[];
 | 
				
			||||||
		const key: string = typeof keyOrFn == 'string' ? keyOrFn :
 | 
						url?: string;
 | 
				
			||||||
			`_${Object.keys(XHR.interceptors).length.toString()}`;
 | 
					}
 | 
				
			||||||
		XHR.interceptors[<any>key] = func;
 | 
					
 | 
				
			||||||
		return () => delete XHR.interceptors[<any>key];
 | 
					export class XHR extends TypedEmitter<XhrEvents> {
 | 
				
			||||||
 | 
						private static headers: {[key: string]: string} = {};
 | 
				
			||||||
 | 
						private static interceptors: {[key: string]: Interceptor} = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private headers: {[key: string]: string} = {}
 | 
				
			||||||
 | 
						private interceptors: {[key: string]: Interceptor} = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						constructor(public readonly opts: XhrOptions = {}) {
 | 
				
			||||||
 | 
							super();
 | 
				
			||||||
 | 
							if(opts.interceptors) {
 | 
				
			||||||
 | 
								opts.interceptors.forEach(i => XHR.addInterceptor(i));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	addInterceptor(fn: FetchInterceptor): () => {};
 | 
						static addInterceptor(fn: Interceptor): () => void {
 | 
				
			||||||
	addInterceptor(key: string, fn: FetchInterceptor): () => {};
 | 
							const key = Object.keys(XHR.interceptors).length.toString();
 | 
				
			||||||
	addInterceptor(keyOrFn: string | FetchInterceptor, fn?: FetchInterceptor): () => {} {
 | 
							XHR.interceptors[key] = fn;
 | 
				
			||||||
		const func: any = fn ? fn : keyOrFn;
 | 
							return () => { XHR.interceptors[key] = <any>null; }
 | 
				
			||||||
		const key: string = typeof keyOrFn == 'string' ? keyOrFn :
 | 
					 | 
				
			||||||
			`_${Object.keys(this.interceptors).length.toString()}`;
 | 
					 | 
				
			||||||
		this.interceptors[<any>key] = func;
 | 
					 | 
				
			||||||
		return () => delete this.interceptors[<any>key];
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	getInterceptors() {
 | 
						addInterceptor(fn: Interceptor): () => void {
 | 
				
			||||||
		return [...Object.values(XHR.interceptors), ...Object.values(this.interceptors)];
 | 
							const key = Object.keys(this.interceptors).length.toString();
 | 
				
			||||||
 | 
							this.interceptors[key] = fn;
 | 
				
			||||||
 | 
							return () => { this.interceptors[key] = <any>null; }
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fetch<T2 = T>(href?: string, body?: any, opts: any = {}): Promise<T2> {
 | 
						async request<T>(opts: RequestOptions = {}): Promise<T>  {
 | 
				
			||||||
		const headers =  {
 | 
							if(!this.opts.url && !opts.url) throw new Error('Momentum server URL needs to be set');
 | 
				
			||||||
			'Content-Type': (body && !(body instanceof FormData)) ? 'application/json' : undefined,
 | 
							const url = (opts.url?.startsWith('http') ? opts.url : (this.opts.url || '') + opts.url).replace(/([^:]\/)\/+/g, '$1');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Prep headers
 | 
				
			||||||
 | 
							const headers = <any>clean({
 | 
				
			||||||
 | 
								'Content-Type': (opts.body && !(opts.body instanceof FormData)) ? 'application/json' : undefined,
 | 
				
			||||||
			...XHR.headers,
 | 
								...XHR.headers,
 | 
				
			||||||
			...this.headers,
 | 
								...this.headers,
 | 
				
			||||||
			...opts.headers
 | 
								...opts.headers
 | 
				
			||||||
		};
 | 
							});
 | 
				
			||||||
		Object.keys(headers).forEach(h => { if(!headers[h]) delete headers[h]; });
 | 
					
 | 
				
			||||||
		return fetch(`${this.baseUrl}${href || ''}`.replace(/([^:]\/)\/+/g, '$1'), {
 | 
							// Send request
 | 
				
			||||||
 | 
							const req = fetch(url, {
 | 
				
			||||||
			headers,
 | 
								headers,
 | 
				
			||||||
			method: opts.method || (body ? 'POST' : 'GET'),
 | 
								method: opts.method || (opts.body ? 'POST' : 'GET'),
 | 
				
			||||||
			body: (headers['Content-Type']?.startsWith('application/json') && body) ? JSON.stringify(body) : body
 | 
								body: (headers['Content-Type']?.startsWith('application/json') && opts.body) ? JSON.stringify(opts.body) : opts.body
 | 
				
			||||||
		}).then(async resp => {
 | 
							}).then(async resp => {
 | 
				
			||||||
			for(let fn of this.getInterceptors()) {
 | 
								for(let fn of [...Object.values(XHR.interceptors), ...Object.values(this.interceptors)]) {
 | 
				
			||||||
				const wait = new Promise(res =>
 | 
									const wait = new Promise(res => fn(resp, () => res(null)));
 | 
				
			||||||
					fn(resp, () => res(null)));
 | 
					 | 
				
			||||||
				await wait;
 | 
									await wait;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if(resp.headers.has('Content-Type')) {
 | 
					
 | 
				
			||||||
				if(resp.headers.get('Content-Type')?.startsWith('application/json')) return await resp.json();
 | 
								this.emit(`${resp.status}`, resp, opts);
 | 
				
			||||||
				if(resp.headers.get('Content-Type')?.startsWith('text/plain')) return await resp.text();
 | 
								if(!resp.ok) throw Error(resp.statusText);
 | 
				
			||||||
			}
 | 
								this.emit('RESPONSE', resp, opts);
 | 
				
			||||||
 | 
								if(resp.headers.get('Content-Type')?.startsWith('application/json')) return await resp.json();
 | 
				
			||||||
 | 
								if(resp.headers.get('Content-Type')?.startsWith('text/plain')) return await <any>resp.text();
 | 
				
			||||||
			return resp;
 | 
								return resp;
 | 
				
			||||||
 | 
							}).catch((err: Error) => {
 | 
				
			||||||
 | 
								this.emit('REJECTED', err, opts);
 | 
				
			||||||
 | 
								throw err;
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
							this.emit('REQUEST', req, opts)
 | 
				
			||||||
 | 
							return req;
 | 
				
			||||||
	delete<T2 = void>(url?: string, opts?: any): Promise<T2> {
 | 
					 | 
				
			||||||
		return this.fetch(url, null, {method: 'delete', ...opts});
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	get<T2 = T>(url?: string, opts?: any): Promise<T2> {
 | 
					 | 
				
			||||||
		return this.fetch(url, null, {method: 'get', ...opts});
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	patch<T2 = T>(data: T2, url?: string, opts?: any): Promise<T2> {
 | 
					 | 
				
			||||||
		return this.fetch(url, data, {method: 'patch', ...opts});
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	post<T2 = T>(data: T2, url?: string, opts?: any): Promise<T2> {
 | 
					 | 
				
			||||||
		return this.fetch(url, data, {method: 'post', ...opts});
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	put<T2 = T>(data: Partial<T2>, url?: string, opts?: any): Promise<T2> {
 | 
					 | 
				
			||||||
		return this.fetch(url, data, {method: 'put', ...opts});
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	new<T2 = T>(href: string, headers: Record<string, string | null>): XHR<T2> {
 | 
					 | 
				
			||||||
		const fetch = new XHR<T2>(`${this.baseUrl}${href}`, {
 | 
					 | 
				
			||||||
			...this.headers,
 | 
					 | 
				
			||||||
			...headers,
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
		Object.entries(this.interceptors).map(([key, value]) =>
 | 
					 | 
				
			||||||
			fetch.addInterceptor(key, value));
 | 
					 | 
				
			||||||
		return fetch;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user