An open API service indexing awesome lists of open source software.

https://github.com/metarhia/metautil

Metarhia utilities ๐Ÿงฐ
https://github.com/metarhia/metautil

hacktoberfest metarhia nodejs utilities

Last synced: 4 months ago
JSON representation

Metarhia utilities ๐Ÿงฐ

Awesome Lists containing this project

README

          

# Metarhia utilities

[![ci status](https://github.com/metarhia/metautil/workflows/Testing%20CI/badge.svg)](https://github.com/metarhia/metautil/actions?query=workflow%3A%22Testing+CI%22+branch%3Amaster)
[![snyk](https://snyk.io/test/github/metarhia/metautil/badge.svg)](https://snyk.io/test/github/metarhia/metautil)
[![npm version](https://badge.fury.io/js/metautil.svg)](https://badge.fury.io/js/metautil)
[![npm downloads/month](https://img.shields.io/npm/dm/metautil.svg)](https://www.npmjs.com/package/metautil)
[![npm downloads](https://img.shields.io/npm/dt/metautil.svg)](https://www.npmjs.com/package/metautil)
[![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/metarhia/metautil/blob/master/LICENSE)

## Usage

- Install: `npm install metautil`
- Require: `const metautil = require('metautil')`

## Async utilities

- `toBool = [() => true, () => false]`
- Example: `const created = await mkdir(path).then(...toBool);`
- `timeout(msec: number, signal?: AbortSignal): Promise`
- `delay(msec: number, signal?: AbortSignal): Promise`
- `timeoutify(promise: Promise, msec: number): Promise`
- `collect(keys: Array, options?: CollectorOptions): Collector`
- `options.exact?: boolean`
- `options.timeout?: number`
- `options.reassign?: boolean`

## Class `Collector`

Async collection is an utility to collect needed keys and signalize on done.

- `constructor(keys: Array, options?: CollectorOptions)`
- `options.exact?: boolean`
- `options.timeout?: number`
- `options.reassign?: boolean`
- `options.defaults?: object`
- `options.validate?: (data: Record) => unknown`
- `set(key: string, value: unknown): void`
- `wait(key: string, fn: AsyncFunction | Promise, ...args?: Array): void`
- `take(key: string, fn: Function, ...args?: Array): void`
- `collect(sources: Record): void`
- `fail(error: Error): void`
- `abort(): void`
- `then(onFulfilled: Function, onRejected?: Function): Promise`
- `done: boolean`
- `data: Dictionary`
- `keys: Array`
- `count: number`
- `exact: boolean`
- `timeout: number`
- `defaults: object`
- `reassign: boolean`
- `validate?: (data: Record) => unknown`
- `signal: AbortSignal`

Collect keys with `.set` method:

```js
const ac = collect(['userName', 'fileName']);

setTimeout(() => ac.set('fileName', 'marcus.txt'), 100);
setTimeout(() => ac.set('userName', 'Marcus'), 200);

const result = await ac;
console.log(result);
```

Collect keys with `.wait` method from async or promise-returning function:

```js
const ac = collect(['user', 'file']);

ac.wait('file', getFilePromisified, 'marcus.txt');
ac.wait('user', getUserPromisified, 'Marcus');

try {
const result = await ac;
console.log(result);
} catch (error) {
console.error(error);
}
```

Collect keys with `.take` method from callback-last-error-first function:

```js
const ac = collect(['user', 'file'], { timeout: 2000, exact: false });

ac.take('file', getFileCallback, 'marcus.txt');
ac.take('user', getUserCallback, 'Marcus');

const result = await ac;
```

Set default values โ€‹โ€‹for unset keys using the `options.defaults` argument:

```js
const defaults = { key1: 'sub1', key2: 'sub1' };

const dc = collect(['key1', 'key2'], { defaults, timeout: 2000 });
dc.set('key2', 'sub2');

const result = await dc;
```

Compose collectors (collect subkeys from multiple sources):

```js
const dc = collect(['key1', 'key2', 'key3']);
const key1 = collect(['sub1']);
const key3 = collect(['sub3']);
dc.collect({ key1, key3 });
const result = await dc;
```

Complex example: compare `Promise.allSettled` + `Promise.race` vs `Collector` in next two examples:

```js
// Collect 4 keys from different contracts with Promise.allSettled + Promise.race

const promise1 = new Promise((resolve, reject) => {
fs.readFile('README.md', (err, data) => {
if (err) return void reject(err);
resolve(data);
});
});
const promise2 = fs.promises.readFile('README.md');
const url = 'http://worldtimeapi.org/api/timezone/Europe';
const promise3 = fetch(url).then((data) => data.json());
const promise4 = new Promise((resolve) => {
setTimeout(() => resolve('value4'), 50);
});
const timeout = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('Timed out')), 1000);
});
const data = Promise.allSettled([promise1, promise2, promise3, promise4]);
try {
const keys = await Promise.race([data, timeout]);
const [key1, key2, key3, key4] = keys.map(({ value }) => value);
const result = { key1, key2, key3, key4 };
console.log(result);
} catch (err) {
console.log(err);
}
```

Compare with:

```js
// Collect 4 keys from different contracts with Collector

const dc = collect(['key1', 'key2', 'key3', 'key4'], { timeout: 1000 });

dc.take('key1', fs.readFile, 'README.md');
dc.wait('key2', fs.promises.readFile, 'README.md');
const url = 'http://worldtimeapi.org/api/timezone/Europe';
dc.wait(
'key3',
fetch(url).then((data) => data.json()),
);
setTimeout(() => dc.set('key4', 'value4'), 50);

try {
const result = await dc;
console.log(result);
} catch (err) {
console.log(err);
}
```

## Crypto utilities

- `cryptoRandom(min?: number, max?: number): number`
- `random(min?: number, max?: number): number`
- `generateUUID(): string`
- `generateKey(possible: string, length: number): string`
- `crcToken(secret: string, key: string): string`
- `generateToken(secret: string, characters: string, length: number): string`
- `validateToken(secret: string, token: string): boolean`
- `serializeHash(hash: Buffer, salt: Buffer): string`
- `deserializeHash(phcString: string): HashInfo`
- `hashPassword(password: string): Promise`
- `validatePassword(password: string, serHash: string): Promise`
- `md5(fileName: string): Promise`
- `getX509names(cert: X509Certificate): Strings`

```js
const x509 = new crypto.X509Certificate(cert);
const domains = metautil.getX509names(x509);
```

## Datetime utilities

- `duration(s: string | number): number`
- `nowDate(date?: Date): string`
- `nowDateTimeUTC(date?: Date, timeSep?: string): string`
- `parseMonth(s: string): number`
- `parseDay(s: string): number`
- `parseEvery(s: string): Every`
- `nextEvent(every: Every, date?: Date): number`

## Error utilities

- Class `Error`
- `constructor(message: string, options?: number | string | ErrorOptions)`
- `options.code?: number | string`
- `options.cause?: Error`
- `message: string`
- `stack: string`
- `code?: number | string`
- `cause?: Error`
- Class `DomainError`
- `constructor(code?: string, options?: number | string | ErrorOptions)`
- `options.code?: number | string`
- `options.cause?: Error`
- `message: string`
- `stack: string`
- `code?: number | string`
- `cause?: Error`
- `toError(errors: Errors): Error`
- `isError(instance: object): boolean`

## File system utilities

- `exists(path: string): Promise`
- `directoryExists(path: string): Promise`
- `fileExists(path: string): Promise`
- `ensureDirectory(path: string): Promise`
- `parsePath(relPath: string): Strings`

## HTTP utilities

- `parseHost(host?: string): string`
- `parseParams(params: string): Cookies`
- `parseCookies(cookie: string): Headers`
- `parseRange(range: string): StreamRange`

## Network utilities

- Deprecated in 4.x: `fetch(url: string, options?: FetchOptions): Promise`
- `receiveBody(stream: IncomingMessage): Promise`
- `ipToInt(ip?: string): number`
- `intToIp(int: number): string`
- `httpApiCall(url: string, options: ApiOptions): Promise`
- `options.method?: HttpMethod`
- `options.headers?: object`
- `options.body?: Body`

## Objects utilities

- `makePrivate(instance: object): object`
- `protect(allowMixins: Strings, ...namespaces: Namespaces): void`
- `jsonParse(buffer: Buffer): Dictionary | null`
- `isHashObject(o: string | number | boolean | object): boolean`
- `flatObject(source: Dictionary, fields: Strings): Dictionary`
- `unflatObject(source: Dictionary, fields: Strings): Dictionary`
- `getSignature(method: Function): Strings`
- `namespaceByPath(namespace: Dictionary, path: string): Dictionary | null`
- `serializeArguments(fields: Strings, args: Dictionary): string`
- `firstKey(obj: Dictionary): string | undefined`
- `isInstanceOf(obj: unknown, constrName: string): boolean`

## Class Pool

- `constructor(options: PoolOptions)`
- `options.timeout?: number`
- `items: Array`
- `free: Array`
- `queue: Array`
- `current: number`
- `size: number`
- `available: number`
- `timeout: number`
- `next(exclusive?: boolean): Promise`
- `add(item: unknown): void`
- `capture(): Promise`
- `release(item: unknown): void`
- `isFree(item: unknown): boolean`

```js
const pool = new metautil.Pool();
pool.add({ a: 1 });
pool.add({ a: 2 });
pool.add({ a: 3 });

if (pool.isFree(obj1)) console.log('1 is free');
const item = await pool.capture();
if (pool.isFree(obj1)) console.log('1 is captured');
const obj = await pool.next();
// obj is { a: 2 }
pool.release(item);
```

## Array utilities

### `sample(array: Array): unknown`

```js
const cards = ['๐Ÿ‚ก', '๐Ÿƒ’', '๐Ÿ‚ฎ', '๐Ÿ‚ท', '๐Ÿƒš'];
const card = sample(cards);
```

### `shuffle(array: Array): Array`

```js
const players = [{ id: 10 }, { id: 12 }, { id: 15 }];
const places = shuffle(players);
```

### `projection(source: object, fields: Array): Array`

```js
const player = { name: 'Marcus', score: 1500, socket };
const playerState = projection(player, ['name', 'score']);
```

## Class Semaphore

- `constructor(options: SemaphoreOptions)`
- `options.concurrency: number`
- `options.size?: number`
- `options.timeout?: number`
- `concurrency: number`
- `counter: number`
- `timeout: number`
- `size: number`
- `empty: boolean`
- `queue: Array`
- `enter(): Promise`
- `leave(): void`

```js
const options = { concurrency: 3, size: 4, timeout: 1500 };
const semaphore = new Semaphore(options);
await semaphore.enter();
// Do something
semaphore.leave();
```

## Strings utilities

- `replace(str: string, substr: string, newstr: string): string`
- `between(s: string, prefix: string, suffix: string): string`
- `split(s: string, separator: string): [string, string]`
- `isFirstUpper(s: string): boolean`
- `isFirstLower(s: string): boolean`
- `isFirstLetter(s: string): boolean`
- `toLowerCamel(s: string): string`
- `toUpperCamel(s: string): string`
- `toLower(s: string): string`
- `toCamel(separator: string): (s: string) => string`
- `spinalToCamel(s: string): string`
- `snakeToCamel(s: string): string`
- `isConstant(s: string): boolean`
- `fileExt(fileName: string): string`
- `parsePath(relPath: string): Strings`
- `trimLines(s: string): string`

## Units utilities

- `bytesToSize(bytes: number): string`
- `sizeToBytes(size: string): number`

```js
const size = bytesToSize(100000000);
const bytes = sizeToBytes(size);
console.log({ size, bytes });
// { size: '100 MB', bytes: 100000000 }
```

| Symbol | zeros | Unit |
| -----: | ----: | --------- |
| yb | 24 | yottabyte |
| zb | 21 | zettabyte |
| eb | 18 | exabyte |
| pb | 15 | petabyte |
| tb | 12 | terabyte |
| gb | 9 | gigabyte |
| mb | 6 | megabyte |
| kb | 3 | kilobyte |

## Class `Emitter`

- Events:
- `constructor(options?: { maxListeners?: number })`
- `emit(eventName: EventName, data: unknown): Promise`
- `on(eventName: EventName, listener: Listener): void`
- `once(eventName: EventName, listener: Listener): void`
- `off(eventName: EventName, listener?: Listener): void`
- Adapters:
- `toPromise(eventName: EventName): Promise`
- `toAsyncIterable(eventName: EventName): AsyncIterable`
- Utilities:
- `clear(eventName?: EventName): void`
- `listeners(eventName?: EventName): Listener[]`
- `listenerCount(eventName?: EventName): number`
- `eventNames(): EventName[]`

Examples:

```js
const ee = new Emitter();
ee.on('eventA', (data) => {
console.log({ data });
// Prints: { data: 'value' }
});
ee.emit('eventA', 'value');
```

```js
const ee = new Emitter();
setTimeout(() => {
ee.emit('eventA', 'value');
}, 100);
const result = await ee.toPromise('eventA');
```

```js
const ee = new Emitter();
passReferenceSomewhere(ee);
const iterable = ee.toAsyncIterable('eventB');
for await (const eventData of iterable) {
console.log({ eventData });
}
```

## License & Contributors

Copyright (c) 2017-2026 [Metarhia contributors](https://github.com/metarhia/metautil/graphs/contributors).
Metautil is [MIT licensed](./LICENSE).\
Metautil is a part of [Metarhia](https://github.com/metarhia) technology stack.