Module @mdf.js/utils

@mdf.js/utils

Node Version Typescript Version Known Vulnerabilities Documentation

netin

Mytra Development Framework - @mdf.js/utils

Collection of tools useful for several different tasks within the @mdf.js ecosystem

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
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 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);

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. 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);

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');

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); // \(\[\.\*\+\?\^\=\!\:\$\{\}\(\)\|\[\]\/\\]\)

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

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'

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] }

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.


Interfaces

ReadEnvOptions
RetryOptions

Type Aliases

Coercible
Format
FormatFunction
LoggerFunction
LoggerInstance
TaskArguments
TaskAsPromise

Variables

MAX_WAIT_TIME
WAIT_TIME

Functions

coerce
deCycle
escapeRegExp
findNodeModule
formatEnv
loadFile
prettyMS
retroCycle
retry
retryBind
wrapOnRetry