npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

typepipe

v1.0.1

Published

A library to create pipelines with contexts and strong type checking.

Downloads

1

Readme

TypePipe

A library to create pipelines with contexts and strong type checking.

Installation

With Node.js and npm installed in your computer run:

npm install typepipe

Documentation

Documentation autogenerated with TypeDoc can be found at https://afresquet.github.io/typepipe/.

Usage

JavaScript:

const { Pipeline } = require("typepipe");

const pipeline = new Pipeline()
	.pipe((value, context, global) => global.effect(value, context.foo))
	.pipe((value, context, global) => value.toString())
	.pipe((value, context, global) => string.padStart(2, "0"));

pipeline.run(1, { foo: 2 }, { effect: (a, b) => a + b }); // "03"

const fn = pipeline.compose();

fn(5, { foo: 4 }, { effect: (a, b) => a - b }); // "01"

TypeScript:

import { Pipeline } from "typepipe";

interface Context {
	foo: number;
}

interface Global {
	effect: (a: number, b: number) => number;
}

const pipeline = new Pipeline<number, Context, Global>()
	.pipe((value, { foo }, { effect }) => effect(value, foo))
	.pipe((value, context, global) => value.toString())
	.pipe((value, context, global) => string.padStart(2, "0")); // TypeScript knows `value` is a string now

pipeline.run(1, { foo: 2 }, { effect: (a, b) => a + b }); // "03"
pipeline.run("1", { foo: 2 }, { effect: (a, b) => a + b }); // Type Error: value should be a number
pipeline.run(1, "context", { effect: (a, b) => a + b }); // Type Error: context should be type Context

const fn = pipeline.compose();

fn(5, { foo: 4 }, { effect: (a, b) => a - b }); // "01"
fn("5", { foo: 4 }, { effect: (a, b) => a + b }); // Type Error: value should be a number
fn(5, { foo: 4 }, "global"); // Type Error: global should be type Global

Context and Global

As you can see in the examples above, the functions have two other parameters: context and global.

You can use them for anything you want really, but the idea is that context is something (often an object) containing relevant values for the current execution, and global has constant values that are shared between all executions.

For example, you could use this on an Express.js server (or any other server library/framework), and have the context be the current req and res objects, and global could be database models and/or libraries.

This makes unit testing really easy, as you can mock both those contexts very easily.

Another example could be a chatbot, where you have context containing data about the new message, and global containing the data about the whole conversation.

This is useful in anything that gets executed multiple times with different inputs, and has to be processed the same way.

Type Checking

This library was created with type checking as the first priority.

It works best with TypeScript, but you can still use it in JavaScript with JSDoc (click to see how).

You should only need to fill in the generics of Pipeline when instanciating it. Then everything will get inferred automatically, step by step, no matter how many funtions you pipe in.

But if you want to extract the functions you pass to your pipelines (and you should in order to unit test them), you can give them a type TypePipe.Function and pass your generics to the type like this:

import { TypePipe } from "typepipe";
import { MyContext, MyGlobal } from "../types";

export const concatenateContexts: TypePipe.Function<
	string,
	string,
	MyContext,
	MyGlobal
> = (value, context, global) => {
	return value + context + global;
};

As you can imagine, this can become very repetitive as you use the library more and more. Not only you would need to pass your Context and Global generics to each TypePipe.Function, but you would also need to pass them to each Pipeline you create.

And what happens if at some point you want to change the generics? It would be better if we could have a centralized place where to control all of this.

Luckily this is very simple to solve. For Pipeline it would be as easy as extending from it and passing the generics to the extends declaration. And for TypePipe.Function it's not any different, we can make an interface that extends from it, again passing the generics to the extends declaration. We can even keep the input value generic and give it a default type.

import { Pipeline, TypePipe } from "typepipe";
import { MyContext, MyGlobal } from "../types";

export default class MyPipeline<Input = string> extends Pipeline<
	Input,
	MyContext,
	MyGlobal
> {}

export interface MyFunction<Input = string, Output = string>
	extends TypePipe.Function<Input, Output, MyContext, MyGlobal> {}

That's it! Now we can use MyPipeline and MyFunction and we can forget about passing the MyContext and MyGlobal generics to each Pipeline and TypePipe.Function we create.

import MyPipeline, { MyFunction } from "./MyPipeline";

const concatenateContexts: MyFunction = (value, context, global) => {
	return value + context + global;
};

const pipeline = new MyPipeline().pipe(concatenateContexts);

const numberToString: MyFunction<number, string> = (value, context, global) => {
	return value.toString();
};

const pipelineNumber = new MyPipeline<number>().pipe(numberToString);

Examples

You can find some examples in the examples

Contribute

  1. Fork the repository and clone it to your computer.
  2. Create a branch: git checkout -b my-new-feature
  3. Install dependencies: npm install
  4. Commit your changes: git commit -am "Add some feature"
  5. Push to the branch: git push origin my-new-feature
  6. Open a pull request :D