Ecosyste.ms: Awesome

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

Awesome Lists | Featured Topics | Projects

https://github.com/takayuki-y5991/pipeline-ts

A minimal library that provides a functional pipeline mechanism in typescript
https://github.com/takayuki-y5991/pipeline-ts

functional-programming typescript

Last synced: 4 days ago
JSON representation

A minimal library that provides a functional pipeline mechanism in typescript

Awesome Lists containing this project

README

        

# PipeLineTS

PipeLineTS is a minimal dependency-free library for composing pipelines in TypeScript. It allows you to compose both synchronous and asynchronous functions, ensuring type safety throughout the pipeline.

## Features

- Type-safe function composition
- Supports both synchronous and asynchronous functions
- Handles success and failure cases with a unified `Result` type

## Installation

You can install this package using pnpm:
```sh
pnpm add ts-dependency-free-pipeline
```

## Usage
Define Result Types and Utility Functions

Create a file named `result.ts` with the following content:

```typescript

export class Success {
readonly isSuccess = true;
readonly isFailure = false;

constructor(public readonly value: T) {}
}

export class Failure {
readonly isSuccess = false;
readonly isFailure = true;

constructor(public readonly error: E) {}
}

export type Result = Success | Failure;
export type AsyncResult = Result | Promise>;

export const isSuccess = (result: Result): result is Success =>
result.isSuccess;
export const isFailure = (result: Result): result is Failure =>
result.isFailure;

export const success = (value: T): Result => new Success(value);
export const failure = (error: E): Result => new Failure(error);

export const of = (value: T): Result => success(value);

export const match =
({
onSuccess,
onFailure,
}: {
onSuccess: (value: T) => RS | Promise;
onFailure: (error: E) => RF | Promise;
}) =>
async (result: Result): Promise =>
isSuccess(result) ? onSuccess(result.value) : onFailure(result.error);

export const fromPromise = async (
promise: Promise
): Promise> => {
try {
const value = await promise;
return success(value);
} catch (error) {
return failure(error as E);
}
};
```

## Define pipe Function

Create a file named pipeline.ts with the following content:

```typescript

import { AsyncResult, isFailure, Result, Success } from "./result";

type Last = T extends [...infer _, infer L] ? L : never;
type PipeFn = (arg: In) => AsyncResult;
type LastReturnType = K extends keyof T
? T[K] extends PipeFn
? S
: never
: never;
type ExtractError = T[number] extends PipeFn
? E
: never;

type Pipeline = {
[K in keyof T]: K extends "0"
? T[K] extends PipeFn
? PipeFn
: never
: T[K] extends PipeFn
? PipeFn, S, E>
: never;
};

/**
* The `pipe` function composes multiple functions, handling both synchronous and asynchronous processes.
* @template T - The tuple of functions.
* @template ExtractError - The error type extracted from the tuple of functions.
* @param {...Pipeline>} fns - The functions to compose.
* @returns {Function} A function that takes the input of the first function and returns a `Promise` resolving to a `Result` of the last function's output type and the error type.
*
* @example
* const pipeline = pipe(
* async (input: string) => success(parseInt(input)),
* async (num: number) => success(num + 1),
* async (num: number) => success(num.toString())
* );
*
* pipeline("42").then(result =>
* match({
* onSuccess: val => console.log("Success:", val),
* onFailure: err => console.log("Error:", err)
* })(result)
* );
*/
export const pipe = <
T extends [PipeFn, ...PipeFn[]]
>(
...fns: Pipeline>
): ((
arg: Parameters[0]
) => Promise<
Result, ExtractError>
>) => {
return async (
arg: Parameters[0]
): Promise<
Result, ExtractError>
> => {
let result: Result> = await fns[0](arg);
for (const fn of fns.slice(1)) {
if (isFailure(result)) break;
result = await fn((result as Success).value);
}
return result;
};
};
```
## Example Usage

```typescript

import { pipe } from './pipeline';
import { success, failure, match, Result } from './result';

// Define your functions
const parseNumber = async (input: string): Promise> =>
isNaN(Number(input)) ? failure("Not a number") : success(Number(input));

const increment = async (num: number): Promise> =>
success(num + 1);

const stringify = async (num: number): Promise> =>
success(num.toString());

// Create a pipeline
const pipeline = pipe(parseNumber, increment, stringify);

// Execute the pipeline
pipeline("42").then(result =>
match({
onSuccess: val => console.log("Success:", val),
onFailure: err => console.log("Error:", err)
})(result)
);
```

## License

This project is licensed under the MIT License