Updated logger & emitter
This commit is contained in:
parent
77e6a40261
commit
c8ccc19996
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ztimson/js-utilities",
|
||||
"version": "0.3.8",
|
||||
"version": "0.4.0",
|
||||
"description": "JavaScript Utility library",
|
||||
"author": "Zak Timson",
|
||||
"license": "MIT",
|
||||
|
@ -42,23 +42,23 @@ export const CliBackground = {
|
||||
}
|
||||
|
||||
export enum LOG_LEVEL {
|
||||
VERBOSE,
|
||||
DEBUG = 0,
|
||||
LOG = 1,
|
||||
ERROR = 0,
|
||||
WARN = 1,
|
||||
INFO = 2,
|
||||
WARN = 3,
|
||||
ERROR = 4,
|
||||
LOG = 3,
|
||||
DEBUG = 4,
|
||||
}
|
||||
|
||||
export type LoggerEvents = TypedEvents & {
|
||||
'VERBOSE': (...args: any[]) => any;
|
||||
'INFO': (...args: any[]) => any;
|
||||
'WARN': (...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> {
|
||||
static LOG_LEVEL: LOG_LEVEL = LOG_LEVEL.VERBOSE;
|
||||
static LOG_LEVEL: LOG_LEVEL = LOG_LEVEL.DEBUG;
|
||||
|
||||
constructor(public readonly namespace?: string) {
|
||||
super();
|
||||
@ -79,35 +79,35 @@ export class Logger extends TypedEmitter<LoggerEvents> {
|
||||
}
|
||||
|
||||
debug(...args: string[]) {
|
||||
if(Logger.LOG_LEVEL > LOG_LEVEL.VERBOSE) return;
|
||||
if(Logger.LOG_LEVEL < LOG_LEVEL.DEBUG) return;
|
||||
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);
|
||||
}
|
||||
|
||||
log(...args: string[]) {
|
||||
if(Logger.LOG_LEVEL > LOG_LEVEL.INFO) return;
|
||||
if(Logger.LOG_LEVEL < LOG_LEVEL.LOG) return;
|
||||
const str = this.format(...args);
|
||||
Logger.emit(LOG_LEVEL.INFO, str);
|
||||
Logger.emit(LOG_LEVEL.LOG, str);
|
||||
console.log(CliEffects.CLEAR + str);
|
||||
}
|
||||
|
||||
info(...args: string[]) {
|
||||
if(Logger.LOG_LEVEL > LOG_LEVEL.INFO) return;
|
||||
if(Logger.LOG_LEVEL < LOG_LEVEL.INFO) return;
|
||||
const str = this.format(...args);
|
||||
Logger.emit(LOG_LEVEL.INFO, str);
|
||||
console.info(CliForeground.BLUE + str + CliEffects.CLEAR);
|
||||
}
|
||||
|
||||
warn(...args: string[]) {
|
||||
if(Logger.LOG_LEVEL > LOG_LEVEL.WARN) return;
|
||||
if(Logger.LOG_LEVEL < LOG_LEVEL.WARN) return;
|
||||
const str = this.format(...args);
|
||||
Logger.emit(LOG_LEVEL.WARN, str);
|
||||
console.warn(CliForeground.YELLOW + str + CliEffects.CLEAR);
|
||||
}
|
||||
|
||||
error(...args: string[]) {
|
||||
if(Logger.LOG_LEVEL > LOG_LEVEL.ERROR) return;
|
||||
if(Logger.LOG_LEVEL < LOG_LEVEL.ERROR) return;
|
||||
const str = this.format(...args);
|
||||
Logger.emit(LOG_LEVEL.ERROR, str);
|
||||
console.error(CliForeground.RED + str + CliEffects.CLEAR);
|
||||
|
136
src/xhr.ts
136
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> {
|
||||
private static interceptors: {[key: number]: FetchInterceptor} = {};
|
||||
static headers: Record<string, string | null> = {};
|
||||
export type Interceptor = (request: Response, next: () => void) => void;
|
||||
|
||||
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,
|
||||
public readonly headers: Record<string, string | null> = {}
|
||||
) { }
|
||||
export type XhrEvents = TypedEvents & {
|
||||
'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): () => {} {
|
||||
const func: any = fn ? fn : keyOrFn;
|
||||
const key: string = typeof keyOrFn == 'string' ? keyOrFn :
|
||||
`_${Object.keys(XHR.interceptors).length.toString()}`;
|
||||
XHR.interceptors[<any>key] = func;
|
||||
return () => delete XHR.interceptors[<any>key];
|
||||
};
|
||||
|
||||
export type XhrOptions = {
|
||||
interceptors?: Interceptor[];
|
||||
url?: string;
|
||||
}
|
||||
|
||||
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): () => {};
|
||||
addInterceptor(key: string, fn: FetchInterceptor): () => {};
|
||||
addInterceptor(keyOrFn: string | FetchInterceptor, fn?: FetchInterceptor): () => {} {
|
||||
const func: any = fn ? fn : keyOrFn;
|
||||
const key: string = typeof keyOrFn == 'string' ? keyOrFn :
|
||||
`_${Object.keys(this.interceptors).length.toString()}`;
|
||||
this.interceptors[<any>key] = func;
|
||||
return () => delete this.interceptors[<any>key];
|
||||
static addInterceptor(fn: Interceptor): () => void {
|
||||
const key = Object.keys(XHR.interceptors).length.toString();
|
||||
XHR.interceptors[key] = fn;
|
||||
return () => { XHR.interceptors[key] = <any>null; }
|
||||
}
|
||||
|
||||
getInterceptors() {
|
||||
return [...Object.values(XHR.interceptors), ...Object.values(this.interceptors)];
|
||||
addInterceptor(fn: Interceptor): () => void {
|
||||
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> {
|
||||
const headers = {
|
||||
'Content-Type': (body && !(body instanceof FormData)) ? 'application/json' : undefined,
|
||||
async request<T>(opts: RequestOptions = {}): Promise<T> {
|
||||
if(!this.opts.url && !opts.url) throw new Error('Momentum server URL needs to be set');
|
||||
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,
|
||||
...this.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,
|
||||
method: opts.method || (body ? 'POST' : 'GET'),
|
||||
body: (headers['Content-Type']?.startsWith('application/json') && body) ? JSON.stringify(body) : body
|
||||
method: opts.method || (opts.body ? 'POST' : 'GET'),
|
||||
body: (headers['Content-Type']?.startsWith('application/json') && opts.body) ? JSON.stringify(opts.body) : opts.body
|
||||
}).then(async resp => {
|
||||
for(let fn of this.getInterceptors()) {
|
||||
const wait = new Promise(res =>
|
||||
fn(resp, () => res(null)));
|
||||
for(let fn of [...Object.values(XHR.interceptors), ...Object.values(this.interceptors)]) {
|
||||
const wait = new Promise(res => fn(resp, () => res(null)));
|
||||
await wait;
|
||||
}
|
||||
if(resp.headers.has('Content-Type')) {
|
||||
|
||||
this.emit(`${resp.status}`, resp, opts);
|
||||
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 resp.text();
|
||||
}
|
||||
if(resp.headers.get('Content-Type')?.startsWith('text/plain')) return await <any>resp.text();
|
||||
return resp;
|
||||
}).catch((err: Error) => {
|
||||
this.emit('REJECTED', err, opts);
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
this.emit('REQUEST', req, opts)
|
||||
return req;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user