Updated logger & emitter
This commit is contained in:
parent
77e6a40261
commit
c8ccc19996
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user