onion-compose
TypeScript icon, indicating that this package has built-in type declarations

2.0.5 • Public • Published

Onion Compose

GitHub Workflow Status Codecov GitHub license

Simple function inspired by koa compose the difference is that this module accepts any number of arguments as opposed to original which accepts only one argument.

Install

npm i onion-compose

Motivation

Composing functions with onion style execution is a very powerful concept, especially in the HTTP framework for composing middleware functions, and I wanted the same middleware execution style as koa.js but with a middleware signature like expressjs, hence this little module has been created.

If you need a refresher on what is onion style composition, check this out:

test('Run order', async () => {
  const original: number[] = []
  const fn1 = async (arr: number[], next: () => void) => {
    arr.push(1)

    await next()

    arr.push(2)

    return 'done'
  }
  const fn2 = async (arr: number[], next: () => void) => {
    arr.push(3)

    await next()

    arr.push(4)
  }

  const result = await compose([fn1, fn2])([original])

  expect(original).toEqual(expect.arrayContaining([1, 3, 4, 2]))
  expect(result).toBe('done')
})

Usage

import compose from 'compose'

const fn1 = async (a: string, b: number, next: () => void) => {
  await next()
}

const fn2 = async (a: string, b: number, next: () => void) => {
  await next()
}

const fn3 = async (a: string, b: number, next: () => void) => {
  await next()
}

await compose([fn1, fn2, fn3])(['foo', 1])
// or
await compose([fn1, fn2])(['foo', 1], fn3)

You can use compose with any number of arguments.

const fn = (
  a: string,
  b: number,
  c: boolean,
  d: string[],
  e: number[],
  _next: () => void
) => undefined

await compose([fn])(['a', 1, true, ['a', 'b'], [1, 2]])

One difference with koa-compose is that because of the arbitrary number of arguments you cannot nest compose inside another compose (which koa-compose can do), but it's very easy to achieve that with the help of a simple function.

//utility function to wrap compose before passing it to another compose
function nest(composeExec) {
  return (...args) => {
    const next = args[args.length - 1]
    args.pop()
    return composeExec(args, next)
  }
}

const fn1 = (a, next) => {
  next()
}

const fn2 = (a, next) => {
  next()
}

const fn3 = (a: string, next: () => void) => {
  next()
}

const nestedCompose = nest(compose([fn1, fn2]))

// run order: fn3, fn, fn2
await compose([fn3, nestedCompose])(['foo'])

Keep in mind that Typescript types can stop working when nesting compose inside another compose if you know how to make the types work, please make a pull request.

All original koa-compose tests are passing, except for the nested compose test (you can pass that one with the help of the utility function above).

Package Sidebar

Install

npm i onion-compose

Weekly Downloads

0

Version

2.0.5

License

MIT

Unpacked Size

26.9 kB

Total Files

13

Last publish

Collaborators

  • ivandotv