The @mdf.js/utils module is a collection of tools useful for several different tasks within the @mdf.js ecosystem. It is a collection of utilities that are used in different parts of the framework, such as the API, the CLI, the documentation...
The list of utilities are:
retry
and retryBind
: A function that allows you to retry a promise a certain number of times before giving up.prettyMS
: A function that converts milliseconds to a human-readable format.loadFile
: A function that loads a file from the file system, logging the process.findNodeModule
: A function that finds a node module in the file system.escapeRegExp
: A function that escapes a string to be used in a regular expression.coerce
: function for data type coercion, specially useful for environment variables and configuration files.camelCase
: function for converting strings to camelCase.cycle
: function for managing circular references in objects.formatEnv
: functions for formatting environment variables.mock
: functions for mocking objects, specially useful for testing in Jest.To install the @mdf.js/utils module, you can use the following commands:
npm install @mdf.js/utils
yarn add @mdf.js/utils
retry
and retryBind
The retry
and retryBind
functions are used to retry a promise a certain number of times before giving up. The difference between both functions is that the retryBind
can bind the context of the promise to a concrete object.
Both functions have similar signatures:
retry
: retry<T>(task: TaskAsPromise<T>, funcArgs: TaskArguments, options: RetryOptions): Promise<T>
.retryBind
: retryBind<T, U>(task: TaskAsPromise<T>, bindTo: U, funcArgs: TaskArguments, options: RetryOptions): Promise<T>
.The common parameters are between both functions are:
task
(TaskAsPromise<T>
): The task to be executed. TaskAsPromise<T>
is alias type for (...args: TaskArguments) => Promise<T>
.funcArgs
(TaskArguments
): The arguments to be passed to the task. TaskArguments
is an alias type for any[]
.options
(RetryOptions
): The options for the retry. RetryOptions
is an interface with the following properties:
logger
(LoggerFunction
): The logger function used for logging retry attempts. LoggerFunction
is an alias type for (error: Crash | Multi | Boom) => void
. Crash
, Multi
, and Boom
are errors defined in the @mdf.js/crash
.waitTime
(number
): The time to wait between retry attempts, in milliseconds. Default is 1000
.maxWaitTime
(number
): The maximum time to wait between retry attempts, in milliseconds. Default is 15000
.abortSignal
(AbortSignal
): The signal to be used to interrupt the retry process. Default is null
.attempts
(number
): The maximum number of retry attempts. Default is Number.MAX_SAFE_INTEGER
.timeout
(number
): Timeout for each try. Default is undefined
.interrupt
(() => boolean
): A function that determines whether to interrupt the retry process. Should return true
to interrupt, false
otherwise. Default is undefined
. Deprecated. Use abortSignal
instead.The extra parameter for the retryBind
function is:
bindTo
(U
): The object to bind the context of the promise to.Simple task:
import { retry, retryBind } from '@mdf.js/utils';
import { Logger } from '@mdf.js/logger';
const logger: Logger = new Logger();
const task = async (a: number, b: number) => {
if (Math.random() < 0.5) {
throw new Error('Random error');
}
return a + b;
};
const funcArgs = [1, 2];
const options = {
logger: logger.crash,
waitTime: 1000,
maxWaitTime: 15000,
attempts: 5,
timeout: 5000,
};
retry(task, [1, 2], options).then(console.log).catch(console.error);
Aborting the retry process can be done using an AbortSignal
:
import { retry } from '@mdf.js/utils';
import { Logger } from '@mdf.js/logger';
const logger: Logger = new Logger();
const controller = new AbortController();
class MyContext {
public c = 10;
public task = async (a: number, b: number) => {
if (Math.random() < 0.5) {
throw new Error('Random error');
}
return a + b;
};
}
const context = new MyContext();
const options = {
logger: logger.crash,
waitTime: 1000,
maxWaitTime: 15000,
attempts: 5,
timeout: 5000,
abortSignal: controller.signal,
};
setTimeout(() => controller.abort(), 3000);
retryBind(context.task, context, [1, 2], options).then(console.log).catch(console.error);
prettyMS
The prettyMS
function is used to convert milliseconds to a human-readable format. It has the following signature:
prettyMS
: (ms: number): string
.import { prettyMS } from '@mdf.js/utils';
console.log(prettyMS(1000)); // 1s
console.log(prettyMS(1000 * 60)); // 1m
console.log(prettyMS(1000 * 60 * 60)); // 1h
console.log(prettyMS(1000 * 60 * 60 * 24)); // 1d
loadFile
The loadFile
function is used to load a file from the file system, logging the process. It has the following signature:
loadFile
: (path: string, logger?: LoggerInstance): Buffer | undefined
. The logger
parameter is optional and is used to log the process, it should be an instance of the LoggerInstance
class from the @mdf.js/logger
package or a simple object with a debug
method that accepts a string.import { loadFile } from '@mdf.js/utils';
const logger = {
debug: (message: string) => console.log(message),
};
const file = loadFile('path/to/file', logger);
findNodeModule
The findNodeModule
function is used to find a node module in the file system. It has the following signature:
findNodeModule
: (module: string, dir?: string): string | undefined
. The dir
parameter is optional and is used to specify the current working directory, default is __dirname
, this means that the search will start from the own module.import { findNodeModule } from '@mdf.js/utils';
const modulePath = findNodeModule('module-name');
escapeRegExp
The escapeRegExp
function is used to get the source of a regular expression pattern and escape it. It has the following signature:
escapeRegExp
: (regex: RexExp): string
. The regex
parameter is the regular expression to escape.import { escapeRegExp } from '@mdf.js/utils';
const escaped = escapeRegExp(/([.*+?^=!:${}()|\[\]\/\\])/g);
console.log(escaped); // \(\[\.\*\+\?\^\=\!\:\$\{\}\(\)\|\[\]\/\\]\)
coerce
The coerce
function is used for data type coercion, specially useful for environment variables and configuration files. It has the following signature:
coerce
: <T extends Coerceable>(env: string | undefined, alternative?: T): T | undefined
. The env
parameter is the value to coerce, and the alternative
parameter is the default value to return if the coercion fails. The Coerceable
type is an alias type for number | boolean | Record<string, any> | any[] | null
.import { coerce } from '@mdf.js/utils';
// process.env['MY_ENV_VAR'] = '1';
const asNumber = coerce<number>(process.env['MY_ENV_VAR'], 10); // Coerce to number, default to 10
// process.env['MY_ENV_VAR'] = 'true' or 'false';
const asBoolean = coerce<boolean>(process.env['MY_ENV_VAR'], true); // Coerce to boolean, default to true
// process.env['MY_ENV_VAR'] = '{"a": 1}';
const asObject = coerce<Record<string, any>>(process.env['MY_ENV_VAR'], { a: 1 }); // Coerce to object, default to { a: 1 }
// process.env['MY_ENV_VAR'] = '[1,2,3]';
const asArray = coerce<any[]>(process.env['MY_ENV_VAR'], [1, 2, 3]); // Coerce to array, default to [1, 2, 3]
// process.env['MY_ENV_VAR'] = 'null' or 'NULL';
const asNull = coerce(process.env['MY_ENV_VAR']); // Coerce to null
camelCase
The camelCase
function is used to convert strings to camelCase. It has the following signature:
camelCase
: (input: string | string[], options?: Options): string
. The input
parameter is the string or array of strings to convert, and the options
parameter is an object with the following properties:
pascalCase
(boolean
): Convert to PascalCase. foo-bar
-> FooBar
. Default is false
.preserveConsecutiveUppercase
(boolean
): Preserve consecutive uppercase characters. foo-BAR
-> fooBAR
. Default is false
.locale
(string | string[]
): The locale parameter indicates the locale to be used to convert to upper/lower case according to any locale-specific case mappings. If multiple locales are given in an array, the best available locale is used. Setting locale: false
ignores the platform locale and uses the Unicode Default Case Conversion algorithm. Default: The host environment’s current locale.import { camelCase } from 'camelCase';
camelCase('foo-bar');
//=> 'fooBar'
camelCase('foo-bar', { pascalCase: true });
//=> 'FooBar'
camelCase('foo-BAR', { preserveConsecutiveUppercase: true });
//=> 'fooBAR'
camelCase('lorem-ipsum', { locale: 'en-US' });
//=> 'loremIpsum'
camelCase('lorem-ipsum', { locale: 'tr-TR' });
//=> 'loremİpsum'
camelCase('lorem-ipsum', { locale: ['en-US', 'en-GB'] });
//=> 'loremIpsum'
camelCase('lorem-ipsum', { locale: ['tr', 'TR', 'tr-TR'] });
//=> 'loremİpsum'
deCycle
and retroCycle
The deCycle
and retroCycle
functions are used to manage circular references in objects. The deCycle
function is used to remove circular references from an object, and the retroCycle
function is used to restore circular references to an object. They have the following signatures:
deCycle
: (object: any, replacer?: (value: any) => any): any
. The object
parameter is the object to remove circular references from, and the replacer
parameter is a function that replaces circular references with a placeholder. Default is undefined
.retroCycle
: (obj: any): any
. The obj
parameter is the object to restore circular references to.import { deCycle, retroCycle } from '@mdf.js/utils';
const obj = { a: 1 };
obj.b = obj;
const deCycled = deCycle(obj);
console.log(deCycled); // { a: 1, b: '$' }
const retroCycled = retroCycle(deCycled);
console.log(retroCycled); // { a: 1, b: [Circular] }
formatEnv
The formatEnv
function is used to read environment variables (process.env
), filter them based on the indicated prefix, and return an object with the values sanitized and the keys formatted based on the specified options. It has the following signatures:
formatEnv
: <T extends Record<string, any> = Record<string, any>>(): T
. Read environment variables (process.env
) and return an object with the values sanitized and the keys formatted.formatEnv
: <T extends Record<string, any> = Record<string, any>>(prefix: string): T
. Read environment variables (process.env
), filter them based on the indicated prefix, and return an object with the values sanitized and the keys formatted.formatEnv
: <T extends Record<string, any> = Record<string, any>>(prefix: string, options: Partial<ReadEnvOptions>): T
. Read environment variables (process.env
), filter them based on the indicated prefix, and return an object with the values sanitized and the keys formatted based on the specified options. The ReadEnvOptions
type is an interface with the following properties:
separator
(string
): The separator to use for nested keys. Default is __
.format
('camelcase' | 'pascalcase' | 'lowercase' | 'uppercase'
): The format to use for the keys. Default is 'camelcase'
.includePrefix
(boolean
): Whether to include the prefix in the keys. Default is false
.formatEnv
: <T extends Record<string, any> = Record<string, any>>(prefix: string, options: Partial<ReadEnvOptions>, source: Record<string, string | undefined>): T
. Process a source, encoded as an environment variables file, filter them based on the indicated prefix, and return an object with the values sanitized and the keys formatted based on the specified options.import { formatEnv } from '@mdf.js/utils';
process.env['MY_OWN_TEST'] = 'test';
const env = {
EXAMPLE_OBJECT: '{"prop": "value"}',
EXAMPLE_ARRAY: '[1,2,3, "string", {"prop": "value"}, 5.2]',
EXAMPLE_INVALID_OBJECT: '{"prop": }"value"}',
EXAMPLE_INVALID_ARRAY: '[1,2,3, "string", ]{"prop": "value"}, 5.2]',
EXAMPLE_TRUE: 'true',
EXAMPLE_FALSE: 'false',
EXAMPLE_INT: '5',
EXAMPLE_NEGATIVE_INT: '-11',
EXAMPLE_FLOAT: '5.2456',
EXAMPLE_NEGATIVE_FLOAT: '-2.4567',
EXAMPLE_INT_ZERO: '0',
EXAMPLE_FLOAT_ZERO: '0.00',
EXAMPLE_NEGATIVE_INT_ZERO: '-0',
EXAMPLE_NEGATIVE_FLOAT_ZERO: '-0.00',
EXAMPLE_STRING: 'example',
EXAMPLE_DEEP__OBJECT__PROPERTY: 'value',
EXAMPLE_NOT_SHOULD_BE_SANITIZED: 5,
};
console.log(formatEnv()); // { myOwnTest: 'test' }
console.log(
formatEnv('EXAMPLE', { separator: '__', format: 'camelcase', includePrefix: false }, env)
);
// {
// object: { prop: 'value' },
// array: [1, 2, 3, 'string', { prop: 'value' }, 5.2],
// invalidObject: '{"prop": }"value"}',
// invalidArray: '[1,2,3, "string", ]{"prop": "value"}, 5.2]',
// true: true,
// false: false,
// int: 5,
// negativeInt: -11,
// float: 5.2456,
// negativeFloat: -2.4567,
// intZero: 0,
// floatZero: 0,
// negativeIntZero: -0,
// negativeFloatZero: -0,
// string: 'example',
// deep: {
// object: {
// property: 'value',
// },
// },
// notShouldBeSanitized: 5,
// }
Copyright 2024 Mytra Control S.L. All rights reserved.
Use of this source code is governed by an MIT-style license that can be found in the LICENSE file or at https://opensource.org/licenses/MIT.