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
andretryBind
: 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
npm install @mdf.js/utils
- Yarn
yarn add @mdf.js/utils
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 forany[]
. -
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
, andBoom
are errors defined in the@mdf.js/crash
. -
waitTime
(number
): The time to wait between retry attempts, in milliseconds. Default is1000
. -
maxWaitTime
(number
): The maximum time to wait between retry attempts, in milliseconds. Default is15000
. -
abortSignal
(AbortSignal
): The signal to be used to interrupt the retry process. Default isnull
. -
attempts
(number
): The maximum number of retry attempts. Default isNumber.MAX_SAFE_INTEGER
. -
timeout
(number
): Timeout for each try. Default isundefined
. -
interrupt
(() => boolean
): A function that determines whether to interrupt the retry process. Should returntrue
to interrupt,false
otherwise. Default isundefined
. Deprecated. UseabortSignal
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);
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
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
. Thelogger
parameter is optional and is used to log the process, it should be an instance of theLoggerInstance
class from the@mdf.js/logger
package or a simple object with adebug
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);
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
. Thedir
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');
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
. Theregex
parameter is the regular expression to escape.
import { escapeRegExp } from '@mdf.js/utils';
const escaped = escapeRegExp(/([.*+?^=!:${}()|\[\]\/\\])/g);
console.log(escaped); // \(\[\.\*\+\?\^\=\!\:\$\{\}\(\)\|\[\]\/\\]\)
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
. Theenv
parameter is the value to coerce, and thealternative
parameter is the default value to return if the coercion fails. TheCoerceable
type is an alias type fornumber | 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
The camelCase
function is used to convert strings to camelCase. It has the following signature:
-
camelCase
:(input: string | string[], options?: Options): string
. Theinput
parameter is the string or array of strings to convert, and theoptions
parameter is an object with the following properties:-
pascalCase
(boolean
): Convert to PascalCase.foo-bar
->FooBar
. Default isfalse
. -
preserveConsecutiveUppercase
(boolean
): Preserve consecutive uppercase characters.foo-BAR
->fooBAR
. Default isfalse
. -
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. Settinglocale: 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'
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
. Theobject
parameter is the object to remove circular references from, and thereplacer
parameter is a function that replaces circular references with a placeholder. Default isundefined
. -
retroCycle
:(obj: any): any
. Theobj
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] }
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. TheReadEnvOptions
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 isfalse
.
-
-
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.