@daisugi/daisugi

Daisugi is a minimalist functional middleware engine.

Usage no npm install needed!

<script type="module">
  import daisugiDaisugi from 'https://cdn.skypack.dev/@daisugi/daisugi';
</script>

README

@daisugi/daisugi

daisugi

Daisugi is a minimalist functional middleware engine.

version

This project is part of the @daisugi monorepo.

Well tested. | Without any external code dependencies and small size.

Daisugi was created with the purpose of organizing your code in an understandable execution pipeline.

Usage

import { daisugi } from '@daisugi/daisugi';

const { sequenceOf } = daisugi();

function addName(arg) {
  return `${arg} Benadryl`;
}

function addLastName(arg) {
  return `${arg} Cumberbatch.`;
}

const handler = sequenceOf([addName, addLastName]);

handler('Hi');
// -> Hi Benadryl Cumberbatch.

Table of contents

Install

Using npm:

npm install @daisugi/daisugi

Using yarn:

yarn add @daisugi/daisugi

:top: back to top

Downstream and downstream/upstream

Daisugi allows both types, perform sequential executions like traditional pipes do, by downstream, to accomplish it you need to use simple functions (handlers).

import { daisugi } from '@daisugi/daisugi';

const { sequenceOf } = daisugi();

function addName(arg) {
  return `${arg} Benadryl.`;
}

sequenceOf([addName])('Hi');
// -> Hi Benadryl.

Or by yielding downstream, then flowing the control back upstream, often used in middleware (like Koa does). This effect is called cascading. To get it, you only need to provide the injectToolkit property to the meta data of the function, that tells to Daisugi include the toolkit with flow utilities (next, nextWith) as the last argument to your function.

import { daisugi } from '@daisugi/daisugi';

const { sequenceOf } = daisugi();

function addName(arg, toolkit) {
  arg.value = `${arg.value} Benadryl`;

  return toolkit.next;
}

addName.meta = {
  injectToolkit: true,
};

function addLastName(arg) {
  return `${arg.value} Cumberbatch.`;
}

sequenceOf([addName])({ value: 'Hi' });
// -> 'Hi Benadryl.'

By default the type used is downstream, its use is more common. But you can always switch to cascading to get more complex behavior (tracing, logger ...). Or you can mix the both types in the same sequence.

:top: back to top

Synchronous and asynchronous

Daisugi allows handlers to be synchronous or asynchronous.

import { daisugi } from '@daisugi/daisugi';

const { sequenceOf } = daisugi();

async function addName(arg, toolkit) {
  return await toolkit.next;
}

addName.meta = {
  injectToolkit: true,
};

async function addName(arg) {
  return `${arg} Benadryl.`;
}

await sequenceOf([addName])('Hi');
// -> Hi Benadryl.

:top: back to top

Nesting

Daisugi allows you to nest as many sequences within each other as needed, because each sequence is nothing more than a new handler.

import { daisugi } from '@daisugi/daisugi';

const { sequenceOf } = daisugi();

function addName(arg) {
  return `${arg} Benadryl`;
}

function addLastName(arg) {
  return `${arg} Cumberbatch.`;
}

sequenceOf([addName, sequenceOf([addLastName])])('Hi');
// -> Hi Benadryl Cumberbatch.

:top: back to top

Flow control

In Daisugi you are the owner of the data flow, for that purpose you have available a few methods:

  • stopPropagationWith, gives you the possibility to stop and exit the execution of the current sequence.
  • failWith, stops the execution and exits from all sequences.
const {
  daisugi,
  stopPropagationWith,
} = require('@daisugi/daisugi');

const { sequenceOf } = daisugi();

function addName(arg) {
  return stopPropagationWith(`${arg} Benadryl.`);
}

function addLastName(arg) {
  return `${arg} Cumberbatch.`;
}

sequenceOf([addName, addLastName])('Hi');
// -> Hi Benadryl.
const { daisugi, failWith } = require('@daisugi/daisugi');

const { sequenceOf } = daisugi();

function addName(arg) {
  return failWith(`${arg} Benadryl`);
}

function addLastName(arg) {
  return `${arg} Cumberbatch.`;
}

sequenceOf([addName, addLastName])('Hi');
// -> Result.error.value<'Hi Benadryl'>.

:top: back to top

Multiple arguments

The title speaks for itself, you can provide to the handlers, nextWith among others, much arguments as needed.

import { daisugi } from '@daisugi/daisugi';

const { sequenceOf } = daisugi();

function addName(arg1, arg2, arg3) {
  return `${arg} ${arg2} ${arg3}.`;
}

sequenceOf([addName])('Hi', 'Benadryl', 'Cumberbatch');
// -> Hi Benadryl Cumberbatch.

:top: back to top

Extendable

Daisugi gives you the freedom to extend any handler at execution time or during initialization, using the decorators.

import { daisugi } from '@daisugi/daisugi';

function decorator(handler) {
  return function addName(arg) {
    handler(`${arg} ${handler.meta.arg}`);
  };
}

const { sequenceOf } = daisugi([decorator]);

function addLastName(arg) {
  return `${arg} Cumberbatch.`;
}

addLastName.meta = {
  arg: 'Benadryl',
};

sequenceOf([addLastName])('Hi');
// -> Hi Benadryl Cumberbatch.

:top: back to top

Goal

Daisugi goal is to keep the core as simple as possible, and extend its functionality through the provided tools.

:top: back to top

Etymology

Daisugi is a Japanese forestry technique, originated in the 14th century, where specially planted cedar trees are pruned heavily to produce "shoots" that become perfectly uniform, straight and completely knot free lumber.

More info: https://twitter.com/wrathofgnon/status/1250287741247426565

:top: back to top

Other projects

Project Version Changelog Description
Kintsugi version changelog Is a set of utilities to help build a fault tolerant services.
Kado version changelog Is a minimal and unobtrusive inversion of control container.
Oza version changelog Is a fast, opinionated, minimalist web framework for NodeJS.
JavaScript style guide

:top: back to top

License

MIT