# fluent-piper

> Type-safe left-to-right functional pipeline composition

Latest version **3.1.0** (published 2024-02-17) · MIT license · 0 weekly downloads

## Install

```sh
npm install fluent-piper
pnpm add fluent-piper
yarn add fluent-piper
bun add fluent-piper
```

## Health

**Score 30/100 (F)** — status: abandoned.

Positive: has types; esm support; no vulnerabilities; high quality score.

Warnings: low downloads.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 3.1.0 |
| Published | 2024-02-17 |
| First published | 2022-06-20 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 0 |
| Unpacked size | 16.1 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 18 |
| Author | Lorefnon |
| Maintainers | lorefnon |

## Links

- npm: https://www.npmjs.com/package/fluent-piper
- Repository: https://github.com/ts-delight/pipe
- Homepage: https://github.com/ts-delight/pipe#readme
- Issues: https://github.com/ts-delight/pipe/issues
- npm.io page: https://npm.io/package/fluent-piper

## Recent versions

- 3.1.0 (latest) — 2024-02-17
- 3.0.0 — 2023-10-02
- 1.0.1 — 2022-07-19
- 1.0.0 — 2022-06-20

## README

# fluent-piper

Have you see code like `dispatch(intUserEvent(await findActiveUser(flatten(ids.map((it) => it.split(","))))))` and felt: **wow, that's ugly**.

Such code is hard to read is because to understand it you need to first unwrap it inside out. The FP world has had a solution for this for quite some time: [functional pipelines](https://fsharpforfunandprofit.com/pipeline/).

This library brings a similar approach to typescript: You can now refactor the above code to be: 

```ts
await pipe(ids)
    .thru(ids => ids.map(it => it.split(",")))
    .thru(flatten)
    .thru(findActiveUser)
    .await()
    .thru(initUserEvent)
    .thru(dispatch)
    .value
```

This is longer, but also easier to read because we can follow the logic top -> down, left -> right in natural reading order.

Also, unlike many similar javascript libraries, this is fully type-safe and does not impose any limitations on the number of steps your chain can have.

## Installation

pnpm (recommended):

```
pnpm install fluent-piper
```

npm: 

```
npm install fluent-piper
```

## Usage - Eager evaluation

We pass an initial value to pipe and chain steps using `.thru` and finally call `.value` to get the result.

```typescript
import {pipe} from "fluent-piper";

const result = pipe(10).thru((i: number) => i + 1).thru((i: number) => `Result: ${i}`).value;
//                   |           ^            |          ^
//                   |___________|            |__________|----- Types of Subsequent output -> input pairs must match
//                         |
//                         Initial input must match input of first step
//
// result: string = "Result: 11"
//         ^        ^
//         |        |__ Result of left-to-right composition
//         |
//         |__ Output type inferred from output type of last step
//
```

In above usage, steps are eagerly evaluated - every step passed to `.thru` gets immediately executed.

### With async steps

We can use `.await()` to ensure that next step receives resolved value instead of a promise

```typescript
import {pipe} from "fluent-piper";

const result = pipe(10)
    .thru(async (i: number) => i + 1)
    .await()
    .thru((i: number) => `Result: ${i}`)
        // ^--- Not a promise
    .value;
// result: Promise<string>
```

## Advanced usage - Lazy Pipelines

There is a lazy API that offers few more features at the cost of higher overhead. The lazy API builds up a chain of thunks that get executed when the final `.value` is called.
Until `.value` is invoked, nothing gets executed.

### Catching exceptions

If some of the steps can throw, we can use `.catch` to handle them within the pipeline and chain subsequent steps

```typescript
import {pipe} from "fluent-piper"

const departmentName = await pipe.lazy({ id: 1})
    .thru(fetchUser)
    .await()
    .thru(fetchDepartment)
    .await()
    .catch(e => {
        // Catch errors thrown from previous steps (sync/async)
        console.error(e);
        return null;
    })
    .thru(dept => dept?.name)
    .value;
```

### Bailing early

A more [railway oriented](https://fsharpforfunandprofit.com/rop/) approach to handle early exits in a type-safe manner is available through `.bailIf`:

```typescript
import {pipe} from "fluent-piper"

const departmentInfo = await pipe.lazy({ id: 1})
    .thru(fetchUser)
    .await()
    .bailIf(
        user => !user.departmentId, // Decide if to bail
        user => ({ type: "unassigned" as const }) // Value to bail with
    )
    .thru(fetchDepartment) // We will reach here only if we didn't bail above
    .await()
    .bailIf(
        isPublic, // (dept) => boolean
        dept => ({ type: "classified" as const })
    )
    .thru(dept => ({ type: "public" as const, name: dept.name })) // Only if dept is public
    .value; // Promise<{ type: "unassigned" } | { type: "classified" } | { type: "public", name: string }>
```

## License

MIT

---
_Source: https://npm.io/package/fluent-piper · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
