/** * Util class, no state, to provide Util method to work with Roles, User, token * This can be used in web, api (nodejs), so make it totally decouple, follow SOLID principle */ export class AuthHelper { constructor(); static get ADMINROLE(): string; /** * merge 2 arrays of roles and reduce to distinct * [1,2,3] & [2, 3, 4] ==> return [1,2,3,4] */ static mergeRoles(arr1: any[], arr2: any[]): any[]; /** * return array of accepted roles. Admin (this.ADMINROLE) will have all roles accepted. * Examples: given "crm,advisor", if user has "crm", this returns [crm] only * given "crm,advisor", if user has "admin", this returns [crm,advisor] * * @param requireRoles required roles, to say user is qualified * @param userRoles roles of user, to validate with requireRoles * @returns array of satisfy roles */ static hasRoles(requireRoles: string, userRoles: string[]): string[]; } /** helper to represent/layout/format text */ export class TextHelper { /** change 1 to 1️⃣ (unicode square box character) */ static representNumberInIconicDigit(numberString: string | null | undefined): string; /** convert true false to yes/no or icon of yes/no */ static boolToYesNo(b?: boolean, withText?: boolean): string; /** * Convert snake_case to camelCase * @param str * @returns */ static toCamelCase(str: string): string; /** convert camelCase to snake_case * @example someHereIsGood ==> some_here_is_good. CAPITALIZED ==> c_a_p_i_t_a_l_i_z_e_d */ static camelToSnakeCase(str: string): string; } export class RandomFactory { /** return a random GUID */ static getGuid(): string; /** by combining ISOTimeString and nanoid */ static createRandomString(): string; /** * random an integer. Max = 10, so return 0 to 10 * @param max the maximum number this func can return * @returns number integer */ static getRandomIntegerTo(max: number): number; /** * random an integer, return value from min to max (include min and max). (0,10) ==> return any integer from 0 to 10 * @returns number integer */ static getRandomIntegerWithin(min: number, max: number): number; /** * return random element inside array * @param {*} arr * @returns */ static getRandomArrayElement(arr: any[]): any; } interface RetryOptions { maxRetries?: number; delay?: number; exponentialBackoff?: boolean; retryOnErrors?: Array; onRetry?: (error: any, attemptNumber: number) => void; } export class CommonHelper { /** * if provide a number or number-string, this will return a number, with fractationDigits * if provided a string ("AT, ATC, ATO") throw exception * NonNumberValue like null, undefined and NaN is treat as 0 * @param {string} numberString * @param {number} fractationDigits number of decimal digit * @returns number */ static toNumber(numberString: number | string, fractationDigits?: number, treatNonNumberValueAs?: number): number; /** * round value to X decimal places https://stackoverflow.com/questions/11832914/how-to-round-to-at-most-2-decimal-places-if-necessary * 19.103857566765578635.toBe(19.1) * 19.143857566765578635.toBe(19.1) * 19.144857566765578635.toBe(19.1) * @param {*} value * @returns */ static roundNumber(value: number, decimalPlaces?: number): number; /** * This retry helper function provides a flexible way to handle transient failures in your code. * Retries a function execution with configurable retry logic * * @param fn The function to execute * @param options Configuration options. Default max 3 retries, delay 1000ms with exponetialBackoff * @returns Result of the function execution */ static retry(fn: Function, options?: RetryOptions): Promise; /** * Continuously call actionFn by setTimeout with interval. The next process will be schedule after current process completed (success or failed) * Interval can be determined (randomly) by intervalFn() and delay between execution can be vary. * @param actionFn support async function * @param DEFAULT_INTERVAL if nothing provided or callbackFn success, this is the interval for running. If adjustment happen, it will not exceed 2*DEFAULT_INTERVAL * @param intervalFn intervalFn(currentDelay, isPreviousRunSuccess, DEFAULT_INTERVAL). if currentDelay is undefined, should return the default. if currentDelay has value, should return next delay. * @param executeImmediately default = false. If true, invoke actionFn() immediately (in the beginning) when calling this function * @param shouldPerformActionFn shouldPerformActionFn(currentDelay, isPreviousRunSuccess, DEFAULT_INTERVAL). this function should return true if you want to perform actionFn when timeout happen. */ static continuousExecuteBySetTimeout(actionFn: Function, DEFAULT_INTERVAL?: number, intervalFn?: (previousDelay: number, isPreviousRunSuccess: boolean, DEFAULT_INTERVAL: number) => number, executeImmediately?: boolean, shouldPerformActionFn?: (_0: number, _1?: boolean, _2?: number) => boolean): Promise<{ timerId: any; delay: number; }>; /** * Create a default delay number (calculate delay based on previous delay and isPreviousRunSuccess). * When calling ContinuousExecuteBySetTimeout() without intervalFn, this func will be used as default implementation. * PreviousRunSuccess ==> return DEFAULT_INTERVAL. * PreviousRunFailed ==> return random * (1.2 to 2.0) * DEFAULT_INTERVAL. * @param {*} previousDelay * @param {*} isPreviousRunSuccess * @returns */ static continuousExecuteBySetTimeoutDefaultIntervalFn(previousDelay: number, isPreviousRunSuccess: boolean, DEFAULT_INTERVAL: number): number; /** by combining ISOTimeString and nanoid * @deprecated use RandomFactory */ static createRandomString: typeof RandomFactory.createRandomString; /** * random an integer. Max = 10, so return 0 to 10 * @param max the maximum number this func can return * @returns number integer * @deprecated use RandomFactory */ static getRandomIntegerTo: typeof RandomFactory.getRandomIntegerTo; /** * random an integer, return value from min to max (include min and max). (0,10) ==> return any integer from 0 to 10 * @returns number integer * @deprecated use RandomFactory */ static getRandomIntegerWithin: typeof RandomFactory.getRandomIntegerWithin; /** * return random element inside array * @param {*} arr * @returns * @deprecated use RandomFactory */ static getRandomArrayElement: typeof RandomFactory.getRandomArrayElement; /** * This will modify the input array https://stackoverflow.com/a/2450976 * @param {*} array * @returns */ static shuffleArray(array: any[]): any[]; /** * check for intersection of number or string * E.g.: [1,2,3], 2 ==> true * E.g.: ["a","b","c"], ["a"] ==> true * E.g.: ["a","b","c"], ["A"], true ==> true, a==A because of ignoreCase * * value (which is not string) is compared by === (null === null, undefined === undefined) * @param firstList * @param otherList accept single value or array * @param ignoreCase if any value is string, cast either values of firstList and otherList toString(), then compare ignore case * @returns boolean true if there is an intersection */ static hasAnyOfIntersection(firstList: number | string | (number | string)[], otherList?: number | string | (number | string)[], ignoreCase?: boolean): boolean; /** * merge 2 arrays of entries and reduce to distinct * [1,2,3] & [2, 3, 4] ==> return [1,2,3,4] * @param {Array} arr1 * @param {Array} arr2 */ static mergeAndDistinct(arr1: any[], arr2: any[]): any[]; /** * return percent of portion to full, (25, 50) ==> 50 */ static percent(portion: number, full: number, fractationDigits?: number): number; /** * from 100 to 110, the diff is 10 (is 10%). This function returns 10 * @returns null if from to is not number */ static diffInPercent(from: number, to: number, fractationDigits?: number): number | null; /** * join all arguments with "/" seperator. * E.g.: JoinPaths("a", b, c) */ static joinPaths(...parts: (string | number | null | undefined)[]): string; /** * Checks if a string is a valid URL. * @param str The string to check. */ static isURL(str: string): boolean; /** * empty string, null, NaN, undefined return "" * or text string which is not a number, return "" * format number to string (usage of PercentValueFormatter can use this) * @param val * @param fractationDigits * @param showPrefixSign * @param showZeroVal * @param suffix * @returns */ static toNumberString(val?: number | string, fractationDigits?: number, showPrefixSign?: boolean, showZeroVal?: boolean, suffix?: string): string; /** * display 1000000 as 1tr, 1000 as 1k * display 1000000 as 1,000,000 (when using en-US locale) * Also round the number after converting (100400 ==> 100k, 100500 ==> 101k) * vi-VN default thounsand separator is , * 0 will be returned as "0" * NaN or "" will be returned as "" * "ATC" (which is cannot be converted to number) will be returned as is "ATC" * @param numberString original number (string) to format. This string must be able to convert to number. * @param unitDividen dividen divide number to this * @param fractationDigits default is 0 (1000 --> 1,000). if 1, 1000,1 --> 1,000.1 * @param unit default is "tr" (triệu đồng VN) * @param locale "en-US" "vi-VN" */ static numberToUnitString(numberString: number | string, unitDividen?: number, fractationDigits?: number, unit?: string, locale?: string): string; /** (from source), create new object contains mapped fields. * @example {a:1, b:2, c:3} with map {a:AA, b:BB} ==> {AA:1, BB:2} (and omit c:3) */ static objectMapKeys(source: Record, keyMap: Record): any; /** "Deep" merge source into target, also return target. * If you want more complex case, use "npm:deepmerge". * @example "a" and "b" is merged normally, the "deep" ability is in the nested object of "c": * {a:1, b:2, c:{c1:1}} with {a:10, c:{c1:11, c2:2}} * ==> {a:10, b:2, c:{c1: 11, c2:2}} */ static deepMerge(target: any, source: any): object; /** * Recursively converts object keys from snake_case to camelCase * @param objOrArray object or array * @returns Transformed object/array with camelCase keys */ static convertKeysToCamelCase(objOrArray: any[] | object): any[] | object; /** * split string into array, remove empty entries, each output string is trimmed * "1,2,3 ,,, 4, 5 ,6" ==> [1,2,3,4,5,6] * @param strCommaSeparated */ static splitByCommaAndTrim(strCommaSeparated?: string): string[]; /** * give you the Date object, from the jsonDateString (return from some API services) * @param jsonDateString string of this format "/Date(2342353453434)/" */ static parseJsonDate(jsonDateString: string): Date; /** get nameof the variable. * @example const myVar = "hello"; nameof({myVar}) ==> "myVar" */ static nameof(variable: Record): string; static isObject>(value: unknown): value is T; static sleep: (ms: number) => Promise; /** @deprecated use TextHelper */ static camelToSnakeCase: typeof TextHelper.camelToSnakeCase; /** @deprecated use TextHelper */ static toCamelCase: typeof TextHelper.toCamelCase; } export class DateTimeHelper { /** * if now is 2002 12 31 14:22, this return 20021231. * @param date * @returns */ static getCurrentYearMonthDayString(date?: Date): string; /** * if now is 14:22, this return 1422. * 9:40AM ==> 0940 * 16:03 (PM) ==> 1603 * @param {Date} date * @returns {string} */ static getCurrentHoursMinutesString(date?: Date): string; /** * if now is 14:22:59, this return 142259. * 9:40AM ==> 094000 * 16:03 (PM) ==> 160300 */ static getCurrentHoursMinutesSecondsString(date?: Date): string; /** * * @returns string the Date string in format yyyyMMdd (in UTC timezone) */ static getCurrentYearMonthDayStringUTC(date?: Date): string; /** * * @returns string the Time string in format HHmm (in UTC timezone) */ static getCurrentHoursMinutesStringUTC(date?: Date): string; /** * * @returns string the Time string in format HHmmss (in UTC timezone) */ static getCurrentHoursMinutesSecondsStringUTC(date?: Date): string; /** full yearmonthdaytime string in UTC timezone, without ":" char (safe for file naming) */ static getCurrentISOStringUTC(): string; /** * return current date time in full format, in specific culture (language) and timezone. * new Date().toLocaleString("vi-VN", { timezone: "Asia/Saigon", hour12: false }) * @param {*} culture * @param {*} timezone * @returns */ static getDatetimeNowString(culture?: string, timezone?: string): string; /** return the DateTime object like it was get with `new Date()` in a host computer in expected timezone */ static getTimeInGMTTimezone(gmtHour?: number): Date; } export class HtmlHelper { /** * * @param {string} bodyHtml * @param {string[]} tags [style, script, svg] * @returns */ static cleanupHtmlTags(bodyHtml: string, tags: string[]): string; } /** Custom Error class, with support extra information about the exception. * * @example const myError = new CustomError('Something went wrong') * console.log(myError.stack) * Error: Something went wrong * at :8:17 */ export class CustomError extends Error { /** original error */ readonly error?: (Error | unknown) | undefined; /** extra information about the exception, attached to this Error */ readonly extra?: any | undefined; errorCode?: number; /** * * @param messageOrErrorCode string for message, or number for errorCode * @param error original error (if any). This will be contained inside this error object * @param extra extra information, attached to this Error. Can add any arbitrary object to provider extra strutured information */ constructor(messageOrErrorCode: string | number, /** original error */ error?: (Error | unknown) | undefined, /** extra information about the exception, attached to this Error */ extra?: any | undefined); }