Fixed tests
Some checks failed
Build / Tag Version (push) Blocked by required conditions
Build / Publish (push) Blocked by required conditions
Build / Build NPM Project (push) Has been cancelled

This commit is contained in:
Zakary Timson 2024-02-07 02:31:16 -05:00
parent ff849b844a
commit 299224088c
35 changed files with 3824 additions and 416 deletions

1
.gitignore vendored
View File

@ -8,6 +8,7 @@ dist
node_modules
uploads
public/momentum*js
junit.xml
# Logs
*.log

67
dist/src/array.d.ts vendored
View File

@ -1,67 +0,0 @@
export declare function addUnique<T>(array: T[], el: T): T[];
export declare function arrayDiff(a: any[], b: any[]): any[];
/**
* Provides a shorthand for sorting arrays of complex objects by a string property
*
* @example
* ```ts
* let arr = [{a: 'Apple', b: 123}, {a: 'Carrot', b: 789}, {a: 'banana', b: 456}];
* arr.sort(caseInsensitiveSort('a'));
* ```
*
* @param {string} prop - Name of property to use, supports dot notation
* @returns {(a, b) => (number)} - Function to handle sort (Meant to be passed to Array.prototype.sort or used in sortFn)
*/
export declare function caseInsensitiveSort(prop: string): (a: any, b: any) => number;
/**
* Recursively flatten nested arrays
*
* @example
* ```ts
* const arr = [
* {label: null, url: '/'},
* {label: 'Model Admin', url: '/model-admin'},
* [
* {label: 'Elements', url: '/model-admin/elements'},
* {label: 'Example', url: null}
* ]
* ];
*
* console.log(flattenArr(arr));
* // Output:
* [
* {label: null, url: '/'},
* {label: 'Model Admin', url: '/model-admin'},
* {label: 'Elements', url: '/model-admin/elements'},
* {label: 'Example', url: null}
* ]
* ```
*
* @param {any[]} arr - n-dimensional array
* @param {any[]} result - Internal use only -- Keeps track of recursion
* @returns {any[]} - Flattened array
*/
export declare function flattenArr(arr: any[], result?: any[]): any[];
/**
* Provides a shorthand for sorting arrays of complex objects
*
* @example
* ```ts
* let arr = [{a: {b: 2}}, {a: {b: 3}}, {a: {b: 1}}];
* arr.sort(sortByProp('a.b'));
* ```
*
* @param {string} prop - Name of property to use, supports dot notation
* @param {boolean} reverse - Reverse the order of the sort
* @returns {(a, b) => (number)} - Function to handle sort (Meant to be passed to Array.prototype.sort)
*/
export declare function sortByProp(prop: string, reverse?: boolean): (a: any, b: any) => number;
export declare function findByProp(prop: string, value: any): (v: any) => boolean;
export declare function makeUnique(arr: any[]): any[];
/**
* Make sure value is an array, if it isn't wrap it in one.
* @param {T[] | T} value Value that should be an array
* @returns {T[]} Value in an array
*/
export declare function makeArray<T>(value: T | T[]): T[];
//# sourceMappingURL=array.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"array.d.ts","sourceRoot":"","sources":["../../src/array.ts"],"names":[],"mappings":"AAEA,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAGnD;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,GAAG,EAAE,CAKnD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,OAC3B,GAAG,KAAK,GAAG,YAM/B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,MAAM,GAAE,GAAG,EAAO,GAAG,GAAG,EAAE,CAGhE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,UAAQ,8BAUvD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,uBAElD;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,SAKpC;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,CAEhD"}

10
dist/src/emitter.d.ts vendored
View File

@ -1,10 +0,0 @@
export type Listener<T> = (event: T) => any;
export declare class Emitter<T> {
private listeners;
constructor();
emit(e: T): void;
listen(fn: Listener<T>): () => {};
listen(key: string, fn: Listener<T>): () => {};
once(fn: Listener<T>): void;
}
//# sourceMappingURL=emitter.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"emitter.d.ts","sourceRoot":"","sources":["../../src/emitter.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,GAAG,CAAC;AAE5C,qBAAa,OAAO,CAAC,CAAC;IACrB,OAAO,CAAC,SAAS,CAAoC;;IAIrD,IAAI,CAAC,CAAC,EAAE,CAAC;IAIT,MAAM,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,MAAM,EAAE;IACjC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,MAAM,EAAE;IAS9C,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC;CAMpB"}

36
dist/src/errors.d.ts vendored
View File

@ -1,36 +0,0 @@
export declare class CustomError extends Error {
static code: number;
private _code?;
get code(): number;
set code(c: number);
constructor(message?: string, code?: number);
static from(err: Error): CustomError;
static instanceof(err: Error): boolean;
toString(): string;
}
export declare class BadRequestError extends CustomError {
static code: number;
constructor(message?: string);
static instanceof(err: Error): boolean;
}
export declare class UnauthorizedError extends CustomError {
static code: number;
constructor(message?: string);
static instanceof(err: Error): boolean;
}
export declare class ForbiddenError extends CustomError {
static code: number;
constructor(message?: string);
static instanceof(err: Error): boolean;
}
export declare class NotFoundError extends CustomError {
static code: number;
constructor(message?: string);
static instanceof(err: Error): boolean;
}
export declare class InternalServerError extends CustomError {
static code: number;
constructor(message?: string);
static instanceof(err: Error): boolean;
}
//# sourceMappingURL=errors.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"AAYA,qBAAa,WAAY,SAAQ,KAAK;IACrC,MAAM,CAAC,IAAI,SAAO;IAElB,OAAO,CAAC,KAAK,CAAC,CAAS;IACvB,IAAI,IAAI,IAAI,MAAM,CAAuD;IACzE,IAAI,IAAI,CAAC,CAAC,EAAE,MAAM,EAAqB;gBAE3B,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM;IAK3C,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,GAAG,WAAW;IAUpC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK;IAI5B,QAAQ;CAGR;AAED,qBAAa,eAAgB,SAAQ,WAAW;IAC/C,MAAM,CAAC,IAAI,SAAO;gBAEN,OAAO,GAAE,MAAsB;IAI3C,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK;CAG5B;AAED,qBAAa,iBAAkB,SAAQ,WAAW;IACjD,MAAM,CAAC,IAAI,SAAO;gBAEN,OAAO,GAAE,MAAuB;IAI5C,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK;CAG5B;AAED,qBAAa,cAAe,SAAQ,WAAW;IAC9C,MAAM,CAAC,IAAI,SAAO;gBAEN,OAAO,GAAE,MAAoB;IAIzC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK;CAG5B;AAED,qBAAa,aAAc,SAAQ,WAAW;IAC7C,MAAM,CAAC,IAAI,SAAO;gBAEN,OAAO,GAAE,MAAoB;IAIzC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK;CAG5B;AAED,qBAAa,mBAAoB,SAAQ,WAAW;IACnD,MAAM,CAAC,IAAI,SAAO;gBAEN,OAAO,GAAE,MAAgC;IAIrD,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK;CAG5B"}

10
dist/src/index.d.ts vendored
View File

@ -1,10 +0,0 @@
export * from './array';
export * from './emitter';
export * from './errors';
export * from './logger';
export * from './misc';
export * from './objects';
export * from './string';
export * from './time';
export * from './xhr';
//# sourceMappingURL=index.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAE1B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,OAAO,CAAC"}

43
dist/src/logger.d.ts vendored
View File

@ -1,43 +0,0 @@
export declare const CliEffects: {
CLEAR: string;
BRIGHT: string;
DIM: string;
UNDERSCORE: string;
BLINK: string;
REVERSE: string;
HIDDEN: string;
};
export declare const CliForeground: {
BLACK: string;
RED: string;
GREEN: string;
YELLOW: string;
BLUE: string;
MAGENTA: string;
CYAN: string;
WHITE: string;
GREY: string;
};
export declare const CliBackground: {
BLACK: string;
RED: string;
GREEN: string;
YELLOW: string;
BLUE: string;
MAGENTA: string;
CYAN: string;
WHITE: string;
GREY: string;
};
export declare class Logger {
readonly namespace: string;
constructor(namespace: string);
private format;
debug(...args: string[]): void;
error(...args: string[]): void;
info(...args: string[]): void;
log(...args: string[]): void;
warn(...args: string[]): void;
verbose(...args: string[]): void;
}
//# sourceMappingURL=logger.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/logger.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU;;;;;;;;CAQtB,CAAA;AAED,eAAO,MAAM,aAAa;;;;;;;;;;CAUzB,CAAA;AAED,eAAO,MAAM,aAAa;;;;;;;;;;CAUzB,CAAA;AAED,qBAAa,MAAM;aACU,SAAS,EAAE,MAAM;gBAAjB,SAAS,EAAE,MAAM;IAE7C,OAAO,CAAC,MAAM;IAId,KAAK,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE;IAIvB,KAAK,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE;IAIvB,IAAI,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE;IAItB,GAAG,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE;IAIrB,IAAI,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE;IAItB,OAAO,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE;CAGzB"}

34
dist/src/misc.d.ts vendored
View File

@ -1,34 +0,0 @@
/**
* Convert data into a form encoded format.
*
* @param {any} data - data to convert
* @returns {string} - Ecodeded form data
*/
export declare function formEncode(data: any): string;
/**
* Get profile image from Gravatar
*
* @param {string} email Account email address
* @returns {string} Gravatar URL
*/
export declare function gravatar(email: string): string;
/** Parts of a URL */
export type ParsedUrl = {
protocol?: string;
subdomain?: string;
domain: string;
host: string;
port?: number;
path?: string;
query?: {
[name: string]: string;
};
fragment?: string;
};
/**
*
* @param {string} url
* @returns {RegExpExecArray}
*/
export declare function urlParser(url: string): ParsedUrl;
//# sourceMappingURL=misc.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"misc.d.ts","sourceRoot":"","sources":["../../src/misc.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM,CAI5C;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,UAGrC;AAED,qBAAqB;AACrB,MAAM,MAAM,SAAS,GAAG;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE;QAAC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;KAAC,CAAA;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAoBhD"}

67
dist/src/objects.d.ts vendored
View File

@ -1,67 +0,0 @@
/**
* Removes any null values from an object in-place
*
* @example
* ```ts
* let test = {a: 0, b: false, c: null, d: 'abc'}
* console.log(clean(test)); // Output: {a: 0, b: false, d: 'abc'}
* ```
*
* @param {T} obj Object reference that will be cleaned
* @returns {Partial<T>} Cleaned object
*/
export declare function clean<T>(obj: T, includeNull?: boolean): Partial<T>;
/**
* Create a deep copy of an object (vs. a shallow copy of references)
*
* Should be replaced by `structuredClone` once released.
*
* @param {T} value Object to copy
* @returns {T} Type
*/
export declare function deepCopy<T>(value: T): T;
/**
* Get/set a property of an object using dot notation
*
* @example
* ```ts
* // Get a value
* const name = dotNotation<string>(person, 'firstName');
* const familyCarMake = dotNotation(family, 'cars[0].make');
* // Set a value
* dotNotation(family, 'cars[0].make', 'toyota');
* ```
*
* @type T Return type
* @param {Object} obj source object to search
* @param {string} prop property name (Dot notation & indexing allowed)
* @param {any} set Set object property to value, omit to fetch value instead
* @return {T} property value
*/
export declare function dotNotation<T>(obj: any, prop: string, set: T): T;
export declare function dotNotation<T>(obj: any, prop: string): T | undefined;
/**
* Check that an object has the following values
*
* @example
* ```ts
* const test = {a: 2, b: 2};
* includes(test, {a: 1}); // true
* includes(test, {b: 1, c: 3}); // false
* ```
*
* @param target Object to search
* @param values Criteria to check against
* @param allowMissing Only check the keys that are available on the target
* @returns {boolean} Does target include all the values
*/
export declare function includes(target: any, values: any, allowMissing?: boolean): boolean;
/**
* Deep check if two objects are equal
*
* @param {any} a - first item to compare
* @param {any} b - second item to compare
* @returns {boolean} True if they match
*/
export declare function isEqual(a: any, b: any): boolean;
//# sourceMappingURL=objects.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"objects.d.ts","sourceRoot":"","sources":["../../src/objects.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,WAAW,UAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,CAUhE;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAEvC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;AAClE,wBAAgB,WAAW,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;AAgBtE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,UAAQ,GAAG,OAAO,CAUhF;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,GAAG,OAAO,CAO/C"}

1
dist/src/redis.d.ts vendored
View File

@ -1 +0,0 @@
//# sourceMappingURL=redis.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../../src/redis.ts"],"names":[],"mappings":""}

72
dist/src/string.d.ts vendored
View File

@ -1,72 +0,0 @@
export declare function countChars(text: string, pattern: RegExp): number;
export declare function createHex(length: number): string;
export declare function formatPhoneNumber(number: string): string;
/**
* Insert a string into another string at a given position
*
* @example
* ```
* console.log(insertAt('Hello world!', ' glorious', 5);
* // Output: Hello glorious world!
* ```
*
* @param {string} target - Parent string you want to modify
* @param {string} str - Value that will be injected to parent
* @param {number} index - Position to inject string at
* @returns {string} - New string
*/
export declare function insertAt(target: string, str: string, index: number): String;
/**
* Generate a string of random characters.
*
* @example
* ```ts
* const random = randomString();
* const randomByte = randomString(8, "01")
* ```
*
* @param {number} length - length of generated string
* @param {string} pool - character pool to generate string from
* @return {string} generated string
*/
export declare function randomString(length: number, pool?: string): string;
/**
* Generate a random string with fine control over letters, numbers & symbols
*
* @example
* ```ts
* const randomLetter = randomString(1, true);
* const randomChar = randomString(1, true, true, true);
* ```
*
* @param {number} length - length of generated string
* @param {boolean} letters - Add letters to pool
* @param {boolean} numbers - Add numbers to pool
* @param {boolean} symbols - Add symbols to pool
* @return {string} generated string
*/
export declare function randomStringBuilder(length: number, letters?: boolean, numbers?: boolean, symbols?: boolean): string;
/**
* Find all substrings that match a given pattern.
*
* Roughly based on `String.prototype.matchAll`.
*
* @param {string} value - String to search.
* @param {RegExp | string} regex - Regular expression to match.
* @return {RegExpExecArray[]} Found matches.
*/
export declare function matchAll(value: string, regex: RegExp | string): RegExpExecArray[];
/**
* Create MD5 hash using native javascript
* @param d String to hash
* @returns {string} Hashed string
*/
export declare function md5(d: any): string;
/**
* Check if email is valid
*
* @param {string} email - Target
* @returns {boolean} - Follows format
*/
export declare function validateEmail(email: string): boolean;
//# sourceMappingURL=string.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"string.d.ts","sourceRoot":"","sources":["../../src/string.ts"],"names":[],"mappings":"AAAA,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,UAEvD;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,UAEvC;AAwBD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,UAI/C;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE3E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,MAAkB,GAAG,MAAM,CAK7E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,UAAQ,EAAE,OAAO,UAAQ,EAAE,OAAO,UAAQ,GAAG,MAAM,CAgB7G;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,eAAe,EAAE,CAiBjF;AAED;;;;GAIG;AACH,wBAAgB,GAAG,CAAC,CAAC,KAAA,UACoC;AAGzD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,WAE1C"}

25
dist/src/time.d.ts vendored
View File

@ -1,25 +0,0 @@
/**
* Calculate the number of milliseconds until date/time
*
* @param {Date | number} date - Target
* @returns {number} - Number of milliseconds until target
*/
export declare function timeUntil(date: Date | number): number;
/**
* Use in conjunction with `await` to pause an async script
*
* @example
* ```ts
* async () => {
* ...
* await sleep(1000) // Pause for 1 second
* ...
* }
* ```
*
* @param {number} ms - Time to pause for in milliseconds
* @returns {Promise<unknown>} - Resolves promise when it's time to resume
*/
export declare function sleep(ms: number): Promise<unknown>;
export declare function formatDate(date: Date | number | string): string;
//# sourceMappingURL=time.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../../src/time.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAErD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,oBAE/B;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,UAUtD"}

22
dist/src/xhr.d.ts vendored
View File

@ -1,22 +0,0 @@
export type FetchInterceptor = (resp: Response, next: () => any) => any;
export declare class XHR<T> {
readonly baseUrl: string;
readonly headers: Record<string, string | null>;
private static interceptors;
static headers: Record<string, string | null>;
private interceptors;
constructor(baseUrl: string, headers?: Record<string, string | null>);
static addInterceptor(fn: FetchInterceptor): () => {};
static addInterceptor(key: string, fn: FetchInterceptor): () => {};
addInterceptor(fn: FetchInterceptor): () => {};
addInterceptor(key: string, fn: FetchInterceptor): () => {};
getInterceptors(): FetchInterceptor[];
fetch<T2 = T>(href?: string, body?: any, opts?: any): Promise<T2>;
delete<T2 = void>(url?: string, opts?: any): Promise<T2>;
get<T2 = T>(url?: string, opts?: any): Promise<T2>;
patch<T2 = T>(data: T2, url?: string, opts?: any): Promise<T2>;
post<T2 = T>(data: T2, url?: string, opts?: any): Promise<T2>;
put<T2 = T>(data: Partial<T2>, url?: string, opts?: any): Promise<T2>;
new<T2 = T>(href: string, headers: Record<string, string | null>): XHR<T2>;
}
//# sourceMappingURL=xhr.d.ts.map

View File

@ -1 +0,0 @@
{"version":3,"file":"xhr.d.ts","sourceRoot":"","sources":["../../src/xhr.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,CAAC;AAExE,qBAAa,GAAG,CAAC,CAAC;aAMW,OAAO,EAAE,MAAM;aACxB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IANzD,OAAO,CAAC,MAAM,CAAC,YAAY,CAAyC;IACpE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAM;IAEnD,OAAO,CAAC,YAAY,CAAyC;gBAEjC,OAAO,EAAE,MAAM,EACxB,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAM;IAG9D,MAAM,CAAC,cAAc,CAAC,EAAE,EAAE,gBAAgB,GAAG,MAAM,EAAE;IACrD,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,gBAAgB,GAAG,MAAM,EAAE;IASlE,cAAc,CAAC,EAAE,EAAE,gBAAgB,GAAG,MAAM,EAAE;IAC9C,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,gBAAgB,GAAG,MAAM,EAAE;IAS3D,eAAe;IAIf,KAAK,CAAC,EAAE,GAAG,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,GAAE,GAAQ,GAAG,OAAO,CAAC,EAAE,CAAC;IA0BrE,MAAM,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,EAAE,CAAC;IAIxD,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,EAAE,CAAC;IAIlD,KAAK,CAAC,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,EAAE,CAAC;IAI9D,IAAI,CAAC,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,EAAE,CAAC;IAI7D,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,EAAE,CAAC;IAIrE,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;CAS1E"}

16
jest.config.js Normal file
View File

@ -0,0 +1,16 @@
module.exports = {
"reporters": ["default", "jest-junit"],
"roots": [
"<rootDir>/tests"
],
"testMatch": [
"**/?(*.)+(spec|test).+(ts|tsx|js)"
],
"transform": {
".+\\.(ts)$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.ts",
"!**/node_modules/**"
],
};

3472
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -13,10 +13,16 @@
"types": "./dist/src/index.d.ts",
"scripts": {
"build": "vite build",
"test": "npx jest",
"test:coverage": "npx jest --coverage",
"watch": "vite build --watch"
},
"devDependencies": {
"@types/jest": "^29.5.12",
"@types/node": "^18.14.0",
"jest": "^29.7.0",
"jest-junit": "^16.0.0",
"ts-jest": "^29.1.2",
"typescript": "^5.3.3",
"vite": "^5.0.12",
"vite-plugin-dts": "^3.7.2"

View File

@ -7,8 +7,8 @@ export function addUnique<T>(array: T[], el: T): T[] {
export function arrayDiff(a: any[], b: any[]): any[] {
return makeUnique([
...a.filter(v1 => !b.includes(v2 => isEqual(v1, v2))),
...b.filter(v1 => !a.includes(v2 => isEqual(v1, v2))),
...a.filter(v1 => !b.includes((v2: any) => isEqual(v1, v2))),
...b.filter(v1 => !a.includes((v2: any) => isEqual(v1, v2))),
]);
}
@ -80,7 +80,7 @@ export function flattenArr(arr: any[], result: any[] = []): any[] {
* @returns {(a, b) => (number)} - Function to handle sort (Meant to be passed to Array.prototype.sort)
*/
export function sortByProp(prop: string, reverse = false) {
return function (a, b) {
return function (a: any, b: any) {
const aVal = dotNotation<any>(a, prop);
const bVal = dotNotation<any>(b, prop);
if(typeof aVal == 'number' && typeof bVal == 'number')
@ -92,7 +92,7 @@ export function sortByProp(prop: string, reverse = false) {
}
export function findByProp(prop: string, value: any) {
return (v) => isEqual(v[prop], value);
return (v: any) => isEqual(v[prop], value);
}
export function makeUnique(arr: any[]) {

View File

@ -52,8 +52,8 @@ export function urlParser(url: string): ParsedUrl {
groups.subdomain = domains.join('.');
}
if(groups.query) {
const split = (<any>groups.query).split('&'), query = {};
split.forEach(q => {
const split = (<any>groups.query).split('&'), query: any = {};
split.forEach((q: any) => {
const [key, val] = q.split('=');
query[key] = val;
});

View File

@ -8,15 +8,16 @@
* ```
*
* @param {T} obj Object reference that will be cleaned
* @param undefinedOnly Ignore null values
* @returns {Partial<T>} Cleaned object
*/
export function clean<T>(obj: T, includeNull = false): Partial<T> {
export function clean<T>(obj: T, undefinedOnly = false): Partial<T> {
if(obj == null) throw new Error("Cannot clean a NULL value");
if(Array.isArray(obj)) {
obj = <any>obj.filter(o => o != null);
} else {
Object.entries(obj).forEach(([key, value]) => {
if(value === undefined || (includeNull || value === null)) delete (<any>obj)[key];
if((undefinedOnly && value === undefined) || (!undefinedOnly && value == null)) delete (<any>obj)[key];
});
}
return <any>obj;
@ -69,6 +70,34 @@ export function dotNotation<T>(obj: any, prop: string, set?: T): T | undefined {
}, obj);
}
/**
* Recursively flatten a nested object, while maintaining key structure.
*
* @example
* ```ts
* const car = {honda: {model: "Civic"}};
* console.log(flattenObj(car)); //Output {honda.model: "Civic"}
* ```
*
* @param obj - Object to flatten
* @param parent - Recursively check if key is a parent key or not
* @param result - Result
* @returns {object} - Flattened object
*/
export function flattenObj(obj: any, parent?: any, result: any = {}) {
if(typeof obj === "object" && !Array.isArray(obj)) {
for(const key of Object.keys(obj)) {
const propName = parent ? parent + '.' + key : key;
if(typeof obj[key] === 'object') {
flattenObj(obj[key], propName, result);
} else {
result[propName] = obj[key];
}
}
return result;
}
}
/**
* Check that an object has the following values
*

View File

@ -138,8 +138,19 @@ export function matchAll(value: string, regex: RegExp | string): RegExpExecArray
* @param d String to hash
* @returns {string} Hashed string
*/
export function md5(d) {
var r = M(V(Y(X(d),8*d.length)));return r.toLowerCase()};function M(d){for(var _,m="0123456789ABCDEF",f="",r=0;r<d.length;r++)_=d.charCodeAt(r),f+=m.charAt(_>>>4&15)+m.charAt(15&_);return f}function X(d){for(var _=Array(d.length>>2),m=0;m<_.length;m++)_[m]=0;for(m=0;m<8*d.length;m+=8)_[m>>5]|=(255&d.charCodeAt(m/8))<<m%32;return _}function V(d){for(var _="",m=0;m<32*d.length;m+=8)_+=String.fromCharCode(d[m>>5]>>>m%32&255);return _}function Y(d,_){d[_>>5]|=128<<_%32,d[14+(_+64>>>9<<4)]=_;for(var m=1732584193,f=-271733879,r=-1732584194,i=271733878,n=0;n<d.length;n+=16){var h=m,t=f,g=r,e=i;f=md5_ii(f=md5_ii(f=md5_ii(f=md5_ii(f=md5_hh(f=md5_hh(f=md5_hh(f=md5_hh(f=md5_gg(f=md5_gg(f=md5_gg(f=md5_gg(f=md5_ff(f=md5_ff(f=md5_ff(f=md5_ff(f,r=md5_ff(r,i=md5_ff(i,m=md5_ff(m,f,r,i,d[n+0],7,-680876936),f,r,d[n+1],12,-389564586),m,f,d[n+2],17,606105819),i,m,d[n+3],22,-1044525330),r=md5_ff(r,i=md5_ff(i,m=md5_ff(m,f,r,i,d[n+4],7,-176418897),f,r,d[n+5],12,1200080426),m,f,d[n+6],17,-1473231341),i,m,d[n+7],22,-45705983),r=md5_ff(r,i=md5_ff(i,m=md5_ff(m,f,r,i,d[n+8],7,1770035416),f,r,d[n+9],12,-1958414417),m,f,d[n+10],17,-42063),i,m,d[n+11],22,-1990404162),r=md5_ff(r,i=md5_ff(i,m=md5_ff(m,f,r,i,d[n+12],7,1804603682),f,r,d[n+13],12,-40341101),m,f,d[n+14],17,-1502002290),i,m,d[n+15],22,1236535329),r=md5_gg(r,i=md5_gg(i,m=md5_gg(m,f,r,i,d[n+1],5,-165796510),f,r,d[n+6],9,-1069501632),m,f,d[n+11],14,643717713),i,m,d[n+0],20,-373897302),r=md5_gg(r,i=md5_gg(i,m=md5_gg(m,f,r,i,d[n+5],5,-701558691),f,r,d[n+10],9,38016083),m,f,d[n+15],14,-660478335),i,m,d[n+4],20,-405537848),r=md5_gg(r,i=md5_gg(i,m=md5_gg(m,f,r,i,d[n+9],5,568446438),f,r,d[n+14],9,-1019803690),m,f,d[n+3],14,-187363961),i,m,d[n+8],20,1163531501),r=md5_gg(r,i=md5_gg(i,m=md5_gg(m,f,r,i,d[n+13],5,-1444681467),f,r,d[n+2],9,-51403784),m,f,d[n+7],14,1735328473),i,m,d[n+12],20,-1926607734),r=md5_hh(r,i=md5_hh(i,m=md5_hh(m,f,r,i,d[n+5],4,-378558),f,r,d[n+8],11,-2022574463),m,f,d[n+11],16,1839030562),i,m,d[n+14],23,-35309556),r=md5_hh(r,i=md5_hh(i,m=md5_hh(m,f,r,i,d[n+1],4,-1530992060),f,r,d[n+4],11,1272893353),m,f,d[n+7],16,-155497632),i,m,d[n+10],23,-1094730640),r=md5_hh(r,i=md5_hh(i,m=md5_hh(m,f,r,i,d[n+13],4,681279174),f,r,d[n+0],11,-358537222),m,f,d[n+3],16,-722521979),i,m,d[n+6],23,76029189),r=md5_hh(r,i=md5_hh(i,m=md5_hh(m,f,r,i,d[n+9],4,-640364487),f,r,d[n+12],11,-421815835),m,f,d[n+15],16,530742520),i,m,d[n+2],23,-995338651),r=md5_ii(r,i=md5_ii(i,m=md5_ii(m,f,r,i,d[n+0],6,-198630844),f,r,d[n+7],10,1126891415),m,f,d[n+14],15,-1416354905),i,m,d[n+5],21,-57434055),r=md5_ii(r,i=md5_ii(i,m=md5_ii(m,f,r,i,d[n+12],6,1700485571),f,r,d[n+3],10,-1894986606),m,f,d[n+10],15,-1051523),i,m,d[n+1],21,-2054922799),r=md5_ii(r,i=md5_ii(i,m=md5_ii(m,f,r,i,d[n+8],6,1873313359),f,r,d[n+15],10,-30611744),m,f,d[n+6],15,-1560198380),i,m,d[n+13],21,1309151649),r=md5_ii(r,i=md5_ii(i,m=md5_ii(m,f,r,i,d[n+4],6,-145523070),f,r,d[n+11],10,-1120210379),m,f,d[n+2],15,718787259),i,m,d[n+9],21,-343485551),m=safe_add(m,h),f=safe_add(f,t),r=safe_add(r,g),i=safe_add(i,e)}return Array(m,f,r,i)}function md5_cmn(d,_,m,f,r,i){return safe_add(bit_rol(safe_add(safe_add(_,d),safe_add(f,i)),r),m)}function md5_ff(d,_,m,f,r,i,n){return md5_cmn(_&m|~_&f,d,_,r,i,n)}function md5_gg(d,_,m,f,r,i,n){return md5_cmn(_&f|m&~f,d,_,r,i,n)}function md5_hh(d,_,m,f,r,i,n){return md5_cmn(_^m^f,d,_,r,i,n)}function md5_ii(d,_,m,f,r,i,n){return md5_cmn(m^(_|~f),d,_,r,i,n)}function safe_add(d,_){var m=(65535&d)+(65535&_);return(d>>16)+(_>>16)+(m>>16)<<16|65535&m}function bit_rol(d,_){return d<<_|d>>>32-_
export function md5(d: string) {
var r = M(V(Y(X(d),8*d.length)));return r.toLowerCase()};
function M(d:any){for(var _,m="0123456789ABCDEF",f="",r=0;r<d.length;r++)_=d.charCodeAt(r),f+=m.charAt(_>>>4&15)+m.charAt(15&_);return f}
function X(d:any){for(var _=Array(d.length>>2),m=0;m<_.length;m++)_[m]=0;for(m=0;m<8*d.length;m+=8)_[m>>5]|=(255&d.charCodeAt(m/8))<<m%32;return _}
function V(d:any){for(var _="",m=0;m<32*d.length;m+=8)_+=String.fromCharCode(d[m>>5]>>>m%32&255);return _}
function Y(d:any,_:any){d[_>>5]|=128<<_%32,d[14+(_+64>>>9<<4)]=_;for(var m=1732584193,f=-271733879,r=-1732584194,i=271733878,n=0;n<d.length;n+=16){var h=m,t=f,g=r,e=i;f=md5_ii(f=md5_ii(f=md5_ii(f=md5_ii(f=md5_hh(f=md5_hh(f=md5_hh(f=md5_hh(f=md5_gg(f=md5_gg(f=md5_gg(f=md5_gg(f=md5_ff(f=md5_ff(f=md5_ff(f=md5_ff(f,r=md5_ff(r,i=md5_ff(i,m=md5_ff(m,f,r,i,d[n+0],7,-680876936),f,r,d[n+1],12,-389564586),m,f,d[n+2],17,606105819),i,m,d[n+3],22,-1044525330),r=md5_ff(r,i=md5_ff(i,m=md5_ff(m,f,r,i,d[n+4],7,-176418897),f,r,d[n+5],12,1200080426),m,f,d[n+6],17,-1473231341),i,m,d[n+7],22,-45705983),r=md5_ff(r,i=md5_ff(i,m=md5_ff(m,f,r,i,d[n+8],7,1770035416),f,r,d[n+9],12,-1958414417),m,f,d[n+10],17,-42063),i,m,d[n+11],22,-1990404162),r=md5_ff(r,i=md5_ff(i,m=md5_ff(m,f,r,i,d[n+12],7,1804603682),f,r,d[n+13],12,-40341101),m,f,d[n+14],17,-1502002290),i,m,d[n+15],22,1236535329),r=md5_gg(r,i=md5_gg(i,m=md5_gg(m,f,r,i,d[n+1],5,-165796510),f,r,d[n+6],9,-1069501632),m,f,d[n+11],14,643717713),i,m,d[n+0],20,-373897302),r=md5_gg(r,i=md5_gg(i,m=md5_gg(m,f,r,i,d[n+5],5,-701558691),f,r,d[n+10],9,38016083),m,f,d[n+15],14,-660478335),i,m,d[n+4],20,-405537848),r=md5_gg(r,i=md5_gg(i,m=md5_gg(m,f,r,i,d[n+9],5,568446438),f,r,d[n+14],9,-1019803690),m,f,d[n+3],14,-187363961),i,m,d[n+8],20,1163531501),r=md5_gg(r,i=md5_gg(i,m=md5_gg(m,f,r,i,d[n+13],5,-1444681467),f,r,d[n+2],9,-51403784),m,f,d[n+7],14,1735328473),i,m,d[n+12],20,-1926607734),r=md5_hh(r,i=md5_hh(i,m=md5_hh(m,f,r,i,d[n+5],4,-378558),f,r,d[n+8],11,-2022574463),m,f,d[n+11],16,1839030562),i,m,d[n+14],23,-35309556),r=md5_hh(r,i=md5_hh(i,m=md5_hh(m,f,r,i,d[n+1],4,-1530992060),f,r,d[n+4],11,1272893353),m,f,d[n+7],16,-155497632),i,m,d[n+10],23,-1094730640),r=md5_hh(r,i=md5_hh(i,m=md5_hh(m,f,r,i,d[n+13],4,681279174),f,r,d[n+0],11,-358537222),m,f,d[n+3],16,-722521979),i,m,d[n+6],23,76029189),r=md5_hh(r,i=md5_hh(i,m=md5_hh(m,f,r,i,d[n+9],4,-640364487),f,r,d[n+12],11,-421815835),m,f,d[n+15],16,530742520),i,m,d[n+2],23,-995338651),r=md5_ii(r,i=md5_ii(i,m=md5_ii(m,f,r,i,d[n+0],6,-198630844),f,r,d[n+7],10,1126891415),m,f,d[n+14],15,-1416354905),i,m,d[n+5],21,-57434055),r=md5_ii(r,i=md5_ii(i,m=md5_ii(m,f,r,i,d[n+12],6,1700485571),f,r,d[n+3],10,-1894986606),m,f,d[n+10],15,-1051523),i,m,d[n+1],21,-2054922799),r=md5_ii(r,i=md5_ii(i,m=md5_ii(m,f,r,i,d[n+8],6,1873313359),f,r,d[n+15],10,-30611744),m,f,d[n+6],15,-1560198380),i,m,d[n+13],21,1309151649),r=md5_ii(r,i=md5_ii(i,m=md5_ii(m,f,r,i,d[n+4],6,-145523070),f,r,d[n+11],10,-1120210379),m,f,d[n+2],15,718787259),i,m,d[n+9],21,-343485551),m=safe_add(m,h),f=safe_add(f,t),r=safe_add(r,g),i=safe_add(i,e)}return Array(m,f,r,i)}
function md5_cmn(d:any,_:any,m:any,f:any,r:any,i:any){return safe_add(bit_rol(safe_add(safe_add(_,d),safe_add(f,i)),r),m)}
function md5_ff(d:any,_:any,m:any,f:any,r:any,i:any,n:any){return md5_cmn(_&m|~_&f,d,_,r,i,n)}
function md5_gg(d:any,_:any,m:any,f:any,r:any,i:any,n:any){return md5_cmn(_&f|m&~f,d,_,r,i,n)}
function md5_hh(d:any,_:any,m:any,f:any,r:any,i:any,n:any){return md5_cmn(_^m^f,d,_,r,i,n)}
function md5_ii(d:any,_:any,m:any,f:any,r:any,i:any,n:any){return md5_cmn(m^(_|~f),d,_,r,i,n)}
function safe_add(d:any,_:any){var m=(65535&d)+(65535&_);return(d>>16)+(_>>16)+(m>>16)<<16|65535&m}
function bit_rol(d:any,_:any){return d<<_|d>>>32-_
}
/**

View File

@ -52,10 +52,10 @@ export class XHR<T> {
fn(resp, () => res(null)));
await wait;
}
if(resp.headers['Content-Type'] && resp.headers.get('Content-Type').startsWith('application/json'))
return await resp.json();
if(resp.headers['Content-Type'] && resp.headers.get('Content-Type').startsWith('text/plain'))
return await resp.text();
if(resp.headers.has('Content-Type')) {
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();
}
return resp;
});
}

82
tests/array.spec.ts Normal file
View File

@ -0,0 +1,82 @@
import {addUnique, caseInsensitiveSort, flattenArr, sortByProp} from '../src';
describe('Array Utilities', () => {
describe('addUnique', () => {
const arr = [1, 2];
test('non-unique', () => {
addUnique(arr, 1);
expect(arr).toStrictEqual([1, 2]);
});
test('unique', () => {
addUnique(arr, 3);
expect(arr).toStrictEqual([1, 2, 3]);
});
});
describe('flattenArr', () => {
test('flat array', () => expect(flattenArr([1, 2])).toStrictEqual([1, 2]));
test('2D array', () => expect(flattenArr([[1, 2], [3, 4]])).toStrictEqual([1, 2, 3, 4]));
test('3D array', () => expect(flattenArr([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])).toStrictEqual([1, 2, 3, 4, 5, 6, 7, 8]));
test('mixed array', () => expect(flattenArr([1, 2, [3, 4], [[5, 6], [7, 8]]])).toStrictEqual([1, 2, 3, 4, 5, 6, 7, 8]));
});
describe('sortByProp', () => {
test('random letters', () => {
let unsorted: any = Array(100).fill(null)
.map(() => String.fromCharCode(Math.round(Math.random() * 25) + 97));
const sorted = unsorted.sort((a: any, b: any) => {
if(a > b) return 1;
if(a < b) return -1;
return 0;
}).map((l: any) => ({a: l}));
unsorted = unsorted.map((l: any) => ({a: l}));
expect(unsorted.sort(sortByProp('a'))).toStrictEqual(sorted);
});
test('random letters reversed', () => {
let unsorted: any = Array(100).fill(null)
.map(() => String.fromCharCode(Math.round(Math.random() * 25) + 97));
const sorted = unsorted.sort((a: any, b: any) => {
if(a > b) return -1;
if(a < b) return 1;
return 0;
}).map((n: any) => ({a: n}));
unsorted = unsorted.map((n: any) => ({a: n}));
expect(unsorted.sort(sortByProp('a', true))).toStrictEqual(sorted);
});
test('random numbers', () => {
let unsorted: any = Array(100).fill(null).map(() => Math.round(Math.random() * 100));
const sorted = unsorted.sort((a: any, b: any) => a - b).map((n: any) => ({a: n}));
unsorted = unsorted.map((n: any) => ({a: n}));
expect(unsorted.sort(sortByProp('a'))).toStrictEqual(sorted);
});
test('random numbers reversed', () => {
let unsorted: any = Array(100).fill(null).map(() => Math.round(Math.random() * 100));
const sorted = unsorted.sort((a: any, b: any) => b - a).map((n: any) => ({a: n}));
unsorted = unsorted.map((n: any) => ({a: n}));
expect(unsorted.sort(sortByProp('a', true))).toStrictEqual(sorted);
});
});
describe('caseInsensitiveSort', () => {
test('non-string property', () => {
const unsorted: any = [{a: 'Apple', b: 123}, {a: 'Carrot', b: 789}, {a: 'banana', b: 456}];
const sorted: any = unsorted.map((u: any) => ({...u}));
expect(unsorted.sort(caseInsensitiveSort('b'))).toStrictEqual(sorted);
});
test('simple strings', () => {
const unsorted: any = [{a: 'Apple'}, {a: 'Carrot'}, {a: 'banana'}];
const sorted: any = unsorted.sort((first: any, second: any) => {
return first.a.toLowerCase().localeCompare(second.a.toLowerCase());
}).map((u: any) => ({...u}));
expect(unsorted.sort(caseInsensitiveSort('a'))).toStrictEqual(sorted);
});
test('alphanumeric strings', () => {
const unsorted: any = [{a: '4pple'}, {a: 'Carrot'}, {a: 'b4n4n4'}];
const sorted: any = unsorted.sort((first: any, second: any) => {
return first.a.toLowerCase().localeCompare(second.a.toLowerCase());
}).map((u: any) => ({...u}));
expect(unsorted.sort(caseInsensitiveSort('a'))).toStrictEqual(sorted);
});
});
});

36
tests/misc.spec.ts Normal file
View File

@ -0,0 +1,36 @@
import {sleep, urlParser} from '../src';
describe('Miscellanies Utilities', () => {
describe('sleep', () => {
test('wait until', async () => {
const wait = ~~(Math.random() * 500);
const time = new Date().getTime();
await sleep(wait);
expect(new Date().getTime()).toBeGreaterThanOrEqual(time + wait);
});
});
describe('urlParser', () => {
test('localhost w/ port', () => {
const parsed = urlParser('http://localhost:4200/some/path?q1=test1&q2=test2#frag');
expect(parsed.protocol).toStrictEqual('http');
expect(parsed.host).toStrictEqual('localhost:4200');
expect(parsed.domain).toStrictEqual('localhost');
expect(parsed.port).toStrictEqual(4200);
expect(parsed.path).toStrictEqual('/some/path');
expect(parsed.query).toStrictEqual({q1: 'test1', q2: 'test2'});
expect(parsed.fragment).toStrictEqual('frag');
});
test('advanced URL', () => {
const parsed = urlParser('https://sub.domain.example.com/some/path?q1=test1&q2=test2#frag');
expect(parsed.protocol).toStrictEqual('https');
expect(parsed.host).toStrictEqual('sub.domain.example.com');
expect(parsed.domain).toStrictEqual('example.com');
expect(parsed.subdomain).toStrictEqual('sub.domain');
expect(parsed.path).toStrictEqual('/some/path');
expect(parsed.query).toStrictEqual({q1: 'test1', q2: 'test2'});
expect(parsed.fragment).toStrictEqual('frag');
});
});
});

111
tests/object.spec.ts Normal file
View File

@ -0,0 +1,111 @@
import {clean, deepCopy, dotNotation, flattenObj, includes, isEqual} from "../src";
describe('Object Utilities', () => {
const TEST_OBJECT = {
a: 1,
b: [
[2, 3],
[4, 5]
],
c: {
d: [
[{e: 6, f: 7}]
],
},
g: {h: 8},
i: () => 9
};
describe('clean', () => {
test('remove null properties', () => {
const a = {a: 1, b: null, c: undefined};
const final = {a: 1};
expect(clean(a)).toEqual(final);
});
test('remove undefined properties', () => {
const a = {a: 1, b: null, c: undefined};
const final = {a: 1, b: null};
expect(clean(a, true)).toEqual(final);
});
});
describe('deepCopy', () => {
const copy = deepCopy(TEST_OBJECT);
test('Array of arrays', () => {
const a = [[1, 2], [3, 4]];
const b = deepCopy(a);
b[0][1] = 5;
expect(a).not.toEqual(b);
});
test('Change array inside object', () => {
copy.b[1] = [1, 1, 1];
expect(copy.b[1]).not.toEqual(TEST_OBJECT.b[1]);
});
test('Change object inside object', () => {
copy.g = {h: Math.random()};
expect(copy.g).not.toEqual(TEST_OBJECT.g);
});
test('Change object property inside nested array', () => {
copy.c.d[0][0].e = -1;
expect(copy.c.d[0][0].e).not.toEqual(TEST_OBJECT.c.d[0][0].e);
});
});
describe('dotNotation', () => {
test('no object or properties', () => {
expect(dotNotation(undefined, 'z')).toStrictEqual(undefined);
expect(dotNotation(TEST_OBJECT, '')).toStrictEqual(undefined);
});
test('invalid property', () => expect(dotNotation(TEST_OBJECT, 'z')).toBeUndefined());
test('by property', () => expect(dotNotation(TEST_OBJECT, 'a')).toBe(TEST_OBJECT.a));
test('by key', () => expect(dotNotation(TEST_OBJECT, '["a"]')).toBe(TEST_OBJECT['a']));
test('by key (single quote)', () => expect(dotNotation(TEST_OBJECT, '[\'a\']')).toBe(TEST_OBJECT['a']));
test('by key (double quote)', () => expect(dotNotation(TEST_OBJECT, '["a"]')).toBe(TEST_OBJECT['a']));
test('by index', () => expect(dotNotation(TEST_OBJECT, 'b[0]')).toBe(TEST_OBJECT.b[0]));
test('by index (2d)', () => expect(dotNotation(TEST_OBJECT, 'b[1][1]')).toBe(TEST_OBJECT.b[1][1]));
test('everything combined', () => expect(dotNotation(TEST_OBJECT, 'c["d"][0][0].e'))
.toBe(TEST_OBJECT.c['d'][0][0].e));
test('set value', () => {
const COPY = JSON.parse(JSON.stringify(TEST_OBJECT));
dotNotation(COPY, 'c["d"][0][0].e', 'test');
expect(COPY['c']['d'][0][0]['e']).toBe('test');
});
test('set new value', () => {
const COPY = JSON.parse(JSON.stringify(TEST_OBJECT));
dotNotation(COPY, 'c.x.y.z', 'test');
expect(COPY['c']['x']['y']['z']).toBe('test');
});
});
describe('includes', () => {
test('simple', () => expect(includes(TEST_OBJECT, {a: 1})).toBeTruthy());
test('nested', () => expect(includes(TEST_OBJECT, {g: {h: 8}})).toBeTruthy());
test('array', () => expect(includes(TEST_OBJECT, {b: [[2]]})).toBeTruthy());
test('nested array', () => expect(includes(TEST_OBJECT, {a: 1, c: {d: [[{e: 6}]]}})).toBeTruthy());
test('wong nested array', () => expect(includes(TEST_OBJECT, {a: 1, c: {d: [{e: 7}]}})).toBeFalsy());
test('wrong value', () => expect(includes(TEST_OBJECT, {a: 1, b: 2})).toBeFalsy());
test('missing value', () => expect(includes(TEST_OBJECT, {a: 1, i: 10})).toBeFalsy());
});
describe('isEqual', () => {
test('boolean equal', () => expect(isEqual(true, true)).toBeTruthy());
test('boolean not-equal', () => expect(isEqual(true, false)).toBeFalsy());
test('number equal', () => expect(isEqual(1, 1)).toBeTruthy());
test('number not-equal', () => expect(isEqual(1, 0)).toBeFalsy());
test('string equal', () => expect(isEqual('abc', 'abc')).toBeTruthy());
test('string not-equal', () => expect(isEqual('abc', '')).toBeFalsy());
test('array equal', () => expect(isEqual([true, 1, 'a'], [true, 1, 'a'])).toBeTruthy());
test('array not-equal', () => expect(isEqual([true, 1, 'a'], [1])).toBeFalsy());
test('object equal', () => expect(isEqual({a: 1, b: 2}, {a: 1, b: 2})).toBeTruthy());
test('object not-equal', () => expect(isEqual({a: 1, b: 2}, {a: 1})).toBeFalsy());
test('complex', () => expect(isEqual(TEST_OBJECT, TEST_OBJECT)).toBeTruthy());
});
describe('flattenObj', () => {
test('simple nested object', () => expect(flattenObj({a: {b: {c: 1}}})).toEqual({"a.b.c": 1}));
test('already flat object', () => expect(flattenObj(TEST_OBJECT['g'])).toEqual(TEST_OBJECT['g']));
test('non-object input', () => expect(flattenObj(TEST_OBJECT['b'])).toBeUndefined());
test('complex nested object', () => expect(flattenObj({a: 1, b: {c: 2}, d: {e: {f: {g: 3}}}}))
.toEqual({"a": 1, "b.c": 2, "d.e.f.g": 3}));
});
});

50
tests/string.spec.ts Normal file
View File

@ -0,0 +1,50 @@
import {matchAll, randomString, randomStringBuilder} from "../src";
describe('String Utilities', () => {
describe('randomString', () => {
test('length', () => expect(randomString(32).length).toStrictEqual(32));
test('distribution', () => {
const charList = '123';
const random = randomString(32, charList);
expect(random.split('').filter(c => c == '1').length).toBeGreaterThan(0);
expect(random.split('').filter(c => c == '2').length).toBeGreaterThan(0);
expect(random.split('').filter(c => c == '3').length).toBeGreaterThan(0);
});
test('binary', () => {
const randomByte = randomString(8, '01');
expect(randomByte.split('').filter(c => c == '0').length).toBeGreaterThan(0);
expect(randomByte.split('').filter(c => c == '1').length).toBeGreaterThan(0);
expect(randomByte.length).toStrictEqual(8);
});
});
describe('randomStringBuilder', () => {
test('length', () => {
const len = ~~(Math.random() * 32);
expect(randomStringBuilder(len, true).length).toStrictEqual(len);
});
test('no length', () => {
expect(randomStringBuilder(0, true)).toStrictEqual('');
});
test('letters only', () =>
expect(/^[a-zA-Z]{10}$/g.test(randomStringBuilder(10, true))).toBeTruthy());
test('numbers only', () =>
expect(/^[0-9]{10}$/g.test(<any>randomStringBuilder(10, false, true))).toBeTruthy());
test('symbols only', () =>
expect(/^[^a-zA-Z0-9]{10}$/g.test(randomStringBuilder(10, false, false, true))).toBeTruthy());
test('everything', () => {
const randomString = randomStringBuilder(30, true, true, true);
expect(/[a-zA-Z]/g.test(randomString)).toBeTruthy();
expect(/[0-9]/g.test(randomString)).toBeTruthy();
expect(/[^a-zA-Z0-9]/g.test(randomString)).toBeTruthy();
});
test('no pool', () =>
expect(() => randomStringBuilder(10, false, false, false)).toThrow());
});
describe('matchAll', () => {
test('using string', () => expect(matchAll('fooBar fooBar FooBar', 'fooBar').length).toBe(2));
test('using regex', () => expect(matchAll('fooBar fooBar FooBar', /fooBar/g).length).toBe(2));
test('using malformed regex', () => expect(() => matchAll('fooBar fooBar FooBar', /fooBar/)).toThrow());
});
});