1.3.1 • Published 5 months ago

@someimportantcompany/utils v1.3.1

Weekly downloads
-
License
MIT
Repository
github
Last release
5 months ago

@someimportantcompany/utils

NPM Test Typescript

A collection of handy utility functions for you & your projects.

Install

$ npm install --save @someimportantcompany/utils
# or
$ yarn add @someimportantcompany/utils

Functions

To import each function, import/require as needed:

import { assert } from '@someimportantcompany/utils';
// or
const { assert } = require('@someimportantcompany/utils');

assert

Shorthand assertion function, based on http-assert-plus but with none of the additional logic or HTTP status code support.

export function assert(
  condition: any,
  err: Error | string | unknown,
  opts?: Record<string, any> | undefined,
): asserts condition;
import { assert } from '@someimportantcompany/utils';

assert(value, new Error('An error occurred'));
assert(value, 'An error occurred');

try {
  const a = 1;
  assert(a === 2, 'Something bad happened');
} catch (err) {
  assert(false, err, { something: 'bad' });
}

promiseEachSeries

Loop over an array in sequence - supporting async functions. A functional cross between Promise.map & Array.prototype.forEach, which iterates in-sequence rather than in-parallel.

export function promiseEachSeries<T>(
  items: T[],
  eachFn: (item: T, i: number, items: T[]) => (void | Promise<void>),
): Promise<void>;
import { promiseEachSeries } from '@someimportantcompany/utils';

const urls = [
  'https://raw.githubusercontent.com/someimportantcompany/utils/main/README.md',
  'https://raw.githubusercontent.com/someimportantcompany/utils/main/LICENSE.md',
];

const results = [];

await promiseEachSeries(urls, async url => {
  const res = await fetch(url);
  results.push(await res.text());
});

promiseMapSeries

Map an array in sequence - supporting async functions. A functional cross between Promise.map & Array.prototype.map, which iterates in-sequence rather than in-parallel.

export function promiseMapSeries<T, U>(
  items: T[],
  mapFn: (item: T, i: number, items: T[]) => (U | Promise<U>),
): Promise<U[]>;
import { promiseMapSeries } from '@someimportantcompany/utils';

const urls = [
  'https://raw.githubusercontent.com/someimportantcompany/utils/main/README.md',
  'https://raw.githubusercontent.com/someimportantcompany/utils/main/LICENSE.md',
];

const results = await promiseMapSeries(urls, async url => {
  const res = await fetch(url);
  return await res.text();
});

promiseReduceSeries

Map an array in sequence - supporting async functions. A functional cross between Promise.map & Array.prototype.reduce, which iterates in-sequence rather than in-parallel.

export function promiseReduceSeries<T>(
  items: T[],
  reduceFn: (acc: T, item: T, i: number, items: T[]) => (T | Promise<T>),
): Promise<T>;
export function promiseReduceSeries<T, U>(
  items: T[],
  reduceFn: (acc: U, item: T, i: number, items: T[]) => (U | Promise<U>),
  initialValue: U,
): Promise<U>;
import { promiseReduceSeries } from '@someimportantcompany/utils';

const urls = [
  '',
  'https://raw.githubusercontent.com/someimportantcompany/utils/main/README.md',
  'https://raw.githubusercontent.com/someimportantcompany/utils/main/LICENSE.md',
];

const results = await promiseReduceSeries(urls, async (list, url) => {
  const res = await fetch(url);
  return list + (await res.text());
});
import { promiseReduceSeries } from '@someimportantcompany/utils';

const urls = [
  'https://raw.githubusercontent.com/someimportantcompany/utils/main/README.md',
  'https://raw.githubusercontent.com/someimportantcompany/utils/main/LICENSE.md',
];

const results = await promiseReduceSeries(urls, async (list, url) => {
  const res = await fetch(url);
  return list.concat([ await res.text() ]);
}, []);
  • You can optionally pass an initialValue for your reducing function.

promiseAllSettled

Loop through all promises with Promise.allSettled, throw an error if one or more rejected.

export function promiseAllSettled<T>(
  input: Iterable<T | PromiseLike<T>>,
): Promise<Awaited<T>[]>;
import { assert, promiseAllSettled } from '@someimportantcompany/utils';

const urls = [
  'https://raw.githubusercontent.com/someimportantcompany/utils/main/README.md',
  'https://raw.githubusercontent.com/someimportantcompany/utils/main/LICENSE.md',
];

const results = await promiseAllSettled(urls.map(async (url, i) => {
  const res = await fetch(url);
  assert(i === 0, new Error('An error occurred with promiseAllSettled'));
  return await res.text();
}));

// First URL will throw an error, but the other one will fetch successfully
// But then the assert error will be thrown cleanly.
import { assert, promiseAllSettled, PromiseSettledManyErrors } from '@someimportantcompany/utils';

const urls = [
  'https://raw.githubusercontent.com/someimportantcompany/utils/main/README.md',
  'https://raw.githubusercontent.com/someimportantcompany/utils/main/LICENSE.md',
  'https://raw.githubusercontent.com/someimportantcompany/utils/main/package.json',
];

const results = await promiseAllSettled(urls.map(async url => {
  const res = await fetch(url);
  assert(url.endsWith('.json'), new Error('An error occurred with promiseAllSettled'));
  return await res.text();
}));

// The first two URLs will throw an error, but the final one will fetch successfully
// But then the PromiseSettledManyErrors error will be thrown cleanly
// With both errors under `err.inner`

createHashMd5

Hash a string or Buffer with MD5.

export function createHashMd5(input: string | Buffer): string
import { createHashMd5 } from '@someimportantcompany/utils';

const value = createHashMd5('hello world');
// value === '6f5902ac237024bdd0c176cb93063dc4'
const value = createHashMd5(Buffer.from('hello world', 'utf8'));
// value === '6f5902ac237024bdd0c176cb93063dc4'

createHashSha1

Hash a string or Buffer with Sha1.

export function createHashSha1(input: string | Buffer): string
import { createHashSha1 } from '@someimportantcompany/utils';

const value = createHashSha1('hello world');
// value === '2aae6c35c94fcfb415dbe95f408b9ce91ee846ed'
const value = createHashSha1(Buffer.from('hello world', 'utf8'));
// value === '2aae6c35c94fcfb415dbe95f408b9ce91ee846ed'

createHashSha256

Hash a string or Buffer with Sha256.

export function createHashSha256(input: string | Buffer): string
import { createHashSha256 } from '@someimportantcompany/utils';

const value = createHashSha256('hello world');
// value === 'b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9'
const value = createHashSha256(Buffer.from('hello world', 'utf8'));
// value === 'b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9'

attempt

Attempt to execute a function, returning null if it throws. Optionally retry a fixed number of times.

export function attempt<T>(
  fn: () => T,
  retries?: number,
): T | null;
export function attempt<T>(
  fn: () => Promise<T>,
  retries?: number,
): Promise<T> | null;
import { assert, attempt } from '@someimportantcompany/utils';

const value = attempt(() => 1 + 1);
// value === 2

const value = attempt(() => {
  assert(Math.random() > 0.5, new Error('Errors half the time (ish)'));
  return 5;
}, 3);
// value === 5 || value === null
// Depending on if the `assert` call throws the error 3 times in a row
  • If you pass an async function or a function that returns a Promise, you'll need to await attempt(...). Otherwise, you can freely drop the extra await.

tryCatch

Attempt to execute a function, and should it throw transform the error before returning. By default, if an error throws then null is returned.

export function tryCatch<T>(
  tryFn: () => T,
): T | null;
export function tryCatch<T>(
  tryFn: () => Promise<T>,
): Promise<T | null>;
export function tryCatch<T>(
  tryFn: () => T,
  catchFn?: (err: any) => U,
): T | U;
export function tryCatch<T, U>(
  tryFn: () => Promise<T>,
  catchFn?: (err: any) => U,
): Promise<T | U>;
import { assert, tryCatch } from '@someimportantcompany/utils';

const value = tryCatch(() => 1 + 1);
// value === 2

const value = tryCatch(() => {
  assert(Math.random() > 0.5, new Error('Errors half the time (ish)'));
  return 5;
}, err => {
  err.title = 'An error occurred';
  throw err;
});
// value === 5 or throws

const value = tryCatch(() => {
  assert(Math.random() > 0.5, new Error('Errors half the time (ish)'));
  return 5;
}, err => {
  return 0;
});
// value === 5 or value === 0
  • If you pass an async function or a function that returns a Promise, you'll need to await tryCatch(...). Otherwise, you can freely drop the extra await.

wait

Using a Promise, wait a specific amount of milliseconds.

export function wait(timeout: number): Promise<void>;
import { wait } from '@someimportantcompany/utils';
await wait(1000); // 1s

// Pairs really well with ms
import ms from 'ms';
await wait(ms('5s'));

aesEncrypt/aesDecrypt

Encrypt/decrypt a string/Buffer using aes-256-ctr. Optionally transform the data before encrypting/decrypting.

export function aesEncrypt(
  key: string,
  plain: string,
): string;
export function aesEncrypt(
  key: string,
  plain: Buffer,
): Buffer;
export function aesEncrypt<T>(
  key: string,
  plain: T,
  transform: (input: T) => string,
): string;
export function aesEncrypt<T>(
  key: string,
  plain: T,
  transform: (input: T) => Buffer,
): Buffer;

export function aesDecrypt(
  key: string,
  encrypted: string,
): string;
export function aesDecrypt(
  key: string,
  encrypted: Buffer,
): Buffer;
export function aesDecrypt<T>(
  key: string,
  encrypted: string,
  transform: (input: string) => T,
): T;
export function aesDecrypt<T>(
  key: string,
  encrypted: Buffer,
  transform: (input: Buffer) => T,
): T;
import { aesEncrypt, aesDecrypt } from '@someimportantcompany/utils';

const key = 'FORTY_TWO';
const plain = 'It\'s an important and popular fact that things are not always what they seem.';
const encrypted = aesEncrypt(key, plain);
const decrypted = aesDecrypt(key, encrypted);
// decrypted === plain

import fs from 'fs'
const key = 'FORTY_TWO';
const plain = fs.readFileSync('./image.jpg');
const encrypted = aesEncrypt(key, plain);
const decrypted = aesDecrypt(key, encrypted);
// decrypted.toString('base64') === plain.toString('base64')

const key = 'FORTY_TWO';
const plain = {
  data: [
    'Space is big. Really big.',
    'You won\'t believe how vastly, hugely, mind-bogglingly big it is.',
  ],
  links: {
    wikiquote: 'https://en.wikiquote.org/wiki/The_Hitchhiker%27s_Guide_to_the_Galaxy_(film)',
  },
};
const encrypted = aesEncrypt(key, plain, JSON.stringify);
const decrypted = aesDecrypt(key, encrypted, JSON.parse);
// decrypted === plain

createAwsCloudwatchLogGroupUrl

Build a URL to a specific CloudWatch Log Group. Optionally set a filter/timeframe to help limit the number of logs loaded.

export function createAwsCloudwatchLogGroupUrl(params: {
  // Required, specify the `groupName`
  groupName: string,

  // Set the stream name to filter logs by a specific Log Stream, if known
  streamName?: string | undefined,
  // Set the AWS region, falls back to process.env.AWS_REGION or "us-east-1"
  region?: string | undefined,
  // Set a filter query (matching CloudWatch syntax) to filter logs on page load
  filterPattern?: string | undefined,

  // Pass two dates to automatically filter between two dates on page load
  between?: [Date, Date] | undefined,
  // Or pass a number of milliseconds to lazily build a window around "now"
  around?: number,
  // But note: `between`/`around` are mutually exclusive

  // Optionally overwrite the base CloudWatch Logs URL
  baseUrl?: string | undefined,
}): string;
const url = createAwsCloudwatchLogGroupUrl({
  groupName: '/ecs/my-service',
});
// https://console.aws.amazon.com/cloudwatch/home?region=us-east-1#logsV2:log-groups
// /log-group/$252Fecs$252Fmy-service/log-events

const url = createAwsCloudwatchLogGroupUrl({
  groupName: '/ecs/my-service',
  streamName: 'SOME-IDENTIFIER',
  around: 500,
});
// 'https://console.aws.amazon.com/cloudwatch/home?region=us-east-1#logsV2:log-groups
// /log-group/$252Fecs$252Fmy-service/log-events/SOME-IDENTIFIER
// $3Fstart$253D1688739366071$2526end$253D1688739367071

const url = createAwsCloudwatchLogGroupUrl({
  groupName: '/ecs/my-service',
  streamName: 'SOME-IDENTIFIER',
  filterPattern: '"REQUEST-ID"',
});
// https://console.aws.amazon.com/cloudwatch/home?region=us-east-1#logsV2:log-groups
// /log-group/$252Fecs$252Fmy-service/log-events/SOME-IDENTIFIER
// $3FfilterPattern$253D$252522REQUEST-ID$252522
  • Useful for error monitoring tools like Sentry or Slack alerts, especially where the environment doesn't natively support this out-of-the-box (e.g. AWS ECS).
  • You can pass a filterPattern if you attach a "request ID" to your logs.
  • If you're looking for a decent logger, check out bunyan.

1.3.1

5 months ago

1.3.0

6 months ago

1.2.0

10 months ago

1.1.1

10 months ago

1.1.0

10 months ago

1.0.0

10 months ago

0.1.0

10 months ago