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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@vr1/flow-r

v2.0.6

Published

Утилита для конвейерно ориентированного программирования. Подобно монаде Either, даёт обработку ошибок без исключений, сквозную передачу параметров, каскадирование.

Readme

@vr1/flow-r

Утилита для конвейерно ориентированного программирования. Подобно монаде Either, даёт обработку ошибок без исключений. А также добавляет сквозную передачу параметров и каскадирование. Позволяет создавать как синхронные так и асинхронные цепочки/рецепты.

Описание

@vr1/flow-r — это библиотека для построения программ с явным и удобным управлением потоком выполнения, обработкой ошибок без использования исключений (throw/catch), и сквозной передачей параметров. Вдохновлена монадами типа Either/Result.

Позволяет организовывать цепочки вызовов («конвейеры») с автоматическим распространением ошибок и удобным контролем над потоком данных.

Установка

npm install @vr1/flow-r

Основные концепции

  • Ошибки без исключений — обработка ошибок происходит явно через возвращаемые значения, без выбрасывания исключений.
  • Поток данных — параметры и ошибки передаются через весь пайплайн без лишней кухонной логики.
  • Каскадирование — рецепты могут использоваться как шаги для вышестоящих рецептов.

Примеры использования

Базовый пример - путь

import { путь } from "@vr1/flow-r";

const результирующая_функция 
  = путь("результирующая_функция")
    .шаг(действие1)
    .шаг(действие2)
    .шаг(действие3);

function действие1(вход: { ключ1: string }): { объект1: Object } {
  // результирующая функция потребует на вход параметр ключ1
  // в виде { ключ1: string }
}

function действие2(вход: { ключ2: number, объект1: Object }): { объект2: Map<string, string> } {
   // результирующая функция потребует на вход параметр ключ2
   // в дополнение к ключ1, результирующий входной параметр будет { ключ1: string, ключ2: number }
   // несмотря на то, что объект1 тут требуется на вход, в результирующую функцию на вход его подавать не надо
   // действие2 на вход его получит из результата действия1
}

function действие3(вход: { объект1: Object, объект2: Map<string, string> }): { поле3: string } {
    // действие3 получит на вход тот объект1, что вернёт действие1, и тот объект2, что вернёт действие2
    // результирующая функция будет возвращать весь набор параметров, проходящий через цепочку
}

// сигнатура результирующей функции автоматически выведется тайпскриптом как следующее
type РезультирующаяФункция = (вход: { ключ1: string, ключ2: number }) => { объект1: Object, объект2: Map<string, string>, поле3: string } | Ошибка;

Если требуется ограничить выходной набор полей только нужным полем, то нужно завершить цепочку действием выход

const результирующая_функция 
  = путь("результирующая_функция")
    .шаг(действие1)
    .шаг(действие2)
    .шаг(действие3)
    .выход(({ поле3 }) => ({ поле3 }));

// сигнатура результирующей функции станет такой
type РезультирующаяФункция = (вход: { ключ1: string, ключ2: number }) => { поле3: string } | Ошибка;

Второй вариант приведёт к аналогичному результату

const результирующая_функция 
  = путь("результирующая_функция")
    .шаг(действие1)
    .шаг(действие2)
    .шаг(действие3)
    .выход("поле3");

Асинхронное выполнение

Путь поддерживает как синхронные шаги, так и асинхронные. Если все шаги синхронные, то и результирующая функция будет синхронной. Если же хоть один из шагов будет асинхронным, то и результирующая функция будет асинхронной.

import { путь } from "@vr1/flow-r";

async function загрузить_пользователя(вход: { ключ_пользователя: string }): { пользователь: Пользователь } {
  // например, асинхронное получение id пользователя по имени
  return { пользователь: await /* код, работающий с БД, например */ };
}

function почтовый_адрес_пользователя(вход:  { пользователь: Пользователь }): { email: string } {
  // синхронный шаг, возвращает email по id
  return { email: `user${вход.ключ_пользователя}@mail.com` };
}

async function отправить_письмо(вход: { email: string, содержимое_письма: string }): Promise<void> {
  await /* код, работающий с email */
}

const отправить_письмо_пользователю =
  путь("отправить_письмо_пользователю")
    .шаг(загрузить_пользователя)
    .шаг(почтовый_адрес_пользователя)
    .шаг(отправить_письмо);

// автоматически будет иметь тип:
// (вход: { ключ_пользователя: string, содержимое_письма: string }) => Promise<{ пользователь: Пользователь, email: string } | Ошибка>;

async function пример() {
  await отправить_письмо_пользователю({ ключ_пользователя: "Пользователь123", содержимое_письма: "здравствуйте, бла-бла" });
}

Для подобных методов, как правило, не нужно, чтобы они что-либо возвращали. Поэтому, чтобы не засорять контекст вызывающей функции можно использовать пустой возврат.

const отправить_письмо_пользователю =
  путь("отправить_письмо_пользователю")
    .шаг(загрузить_пользователя)
    .шаг(почтовый_адрес_пользователя)
    .шаг(отправить_письмо)
    .выход();
// автоматически будет иметь тип:
// (вход: { ключ_пользователя: string, содержимое_письма: string }) => Promise<{} | Ошибка>;

Обработка ошибок

Как и монада Either, flow-r работает с ошибками, избегая исключений. Любой из шагов цепочки может вернуть ошибку, и тогда выполнение всей цепочки прервётся и функция, представляющая собой цепочку сразу вернёт эту ошибку.

Параллельное выполнение - охват

Иногда нужно выполнить несколько независимых действий параллельно и объединить их результаты. Для этого используется функция и (или охват для именованного варианта).

import { и, путь } from "@vr1/flow-r";

function загрузить_профиль(вход: { id_пользователя: number }): { профиль: Профиль } {
  // загрузка профиля из БД
  return { профиль: /* ... */ };
}

function загрузить_настройки(вход: { id_пользователя: number }): { настройки: Настройки } {
  // загрузка настроек из БД
  return { настройки: /* ... */ };
}

function загрузить_уведомления(вход: { id_пользователя: number }): { уведомления: Уведомление[] } {
  // загрузка уведомлений из БД
  return { уведомления: /* ... */ };
}

// Все три функции выполнятся параллельно, результаты объединятся
const загрузить_данные_пользователя =
  и(загрузить_профиль)
    .и(загрузить_настройки)
    .и(загрузить_уведомления);

// Тип результата автоматически выведется как:
// (вход: { id_пользователя: number }) => { профиль: Профиль, настройки: Настройки, уведомления: Уведомление[] } | Ошибка

Если любая из параллельных функций вернёт ошибку, весь охват вернёт ошибку с информацией обо всех ошибках.

Охват можно использовать как шаг в пути:

const показать_дашборд =
  путь("показать_дашборд")
    .шаг(и(загрузить_профиль).и(загрузить_настройки).и(загрузить_уведомления))
    .шаг(отрендерить_дашборд);

Ограничение выхода работает аналогично пути:

const загрузить_только_профиль =
  и(загрузить_профиль)
    .и(загрузить_настройки)
    .выход("профиль");

// или через селектор
const загрузить_только_профиль_2 =
  и(загрузить_профиль)
    .и(загрузить_настройки)
    .выход(({ профиль }) => ({ профиль }));

Условное выполнение - если

Для выполнения шага только при выполнении условия используется функция если. Предикат должен возвращать объект { условие: boolean }.

import { если, путь } from "@vr1/flow-r";

function пользователь_премиум(вход: { пользователь: Пользователь }): { условие: boolean } {
  return { условие: вход.пользователь.тариф === "премиум" };
}

function загрузить_премиум_контент(вход: { пользователь: Пользователь }): { контент: Контент } {
  return { контент: /* премиум контент */ };
}

function загрузить_базовый_контент(вход: { пользователь: Пользователь }): { контент: Контент } {
  return { контент: /* базовый контент */ };
}

// Только то (без иначе) — если условие false, результат будет undefined
const загрузить_бонус =
  если(пользователь_премиум)
    .то(загрузить_премиум_контент);

// С веткой иначе — всегда выполнится один из вариантов
const загрузить_контент_по_тарифу =
  если(пользователь_премиум)
    .то(загрузить_премиум_контент)
    .иначе(загрузить_базовый_контент);

Использование в пути:

const показать_контент =
  путь("показать_контент")
    .шаг(загрузить_пользователя)
    .шаг(
      если(пользователь_премиум)
        .то(загрузить_премиум_контент)
        .иначе(загрузить_базовый_контент)
    )
    .шаг(отобразить_контент);

Прерывание по условию - ошибка_если

Функция ошибка_если позволяет прервать выполнение цепочки, если выполняется определённое условие. Полезно для валидации входных данных или проверки бизнес-правил.

import { ошибка_если, путь } from "@vr1/flow-r";

function пользователь_заблокирован(вход: { пользователь: Пользователь }): { условие: boolean } {
  return { условие: вход.пользователь.статус === "заблокирован" };
}

function баланс_недостаточен(вход: { счёт: Счёт, сумма: number }): { условие: boolean } {
  return { условие: вход.счёт.баланс < вход.сумма };
}

// Если условие true — цепочка прервётся с ошибкой
const выполнить_перевод =
  путь("выполнить_перевод")
    .шаг(загрузить_пользователя)
    .шаг(ошибка_если(пользователь_заблокирован, "Пользователь заблокирован"))
    .шаг(загрузить_счёт)
    .шаг(ошибка_если(баланс_недостаточен, "Недостаточно средств на счёте"))
    .шаг(провести_транзакцию);

Если пользователь_заблокирован вернёт { условие: true }, то функция выполнить_перевод сразу вернёт ошибку с текстом "Пользователь заблокирован", а шаги загрузить_счёт и провести_транзакцию не будут выполнены.

Второй параметр (текст ошибки) опционален:

// Без указания текста ошибки будет использован текст по умолчанию
.шаг(ошибка_если(пользователь_заблокирован))

Ограничения

Зарезервированные поля

Некоторые имена полей зарезервированы библиотекой и не должны использоваться во входных и выходных объектах ваших функций:

| Поле | Причина | |------|---------| | __ошибка | Используется внутри библиотеки для маркировки объектов ошибок. Если ваш объект содержит это поле, он будет воспринят как ошибка и прервёт выполнение цепочки. | | условие | Зарезервировано для предикатов (если, ошибка_если). Предикаты обязаны возвращать объект вида { условие: boolean }. Использование этого поля в обычных шагах может привести к конфликтам. | | шаг | Зарезервировано как метод fluent API для добавления шагов в путь. | | и | Зарезервировано как метод fluent API для добавления параллельных действий в охват. | | выход | Зарезервировано как метод fluent API для ограничения выходных полей. |

// ❌ Плохо — поле __ошибка зарезервировано
function плохой_шаг(вход: { данные: string }): { __ошибка: string } {
  return { __ошибка: "что-то" }; // будет воспринято как ошибка!
}

// ❌ Плохо — поле условие зарезервировано для предикатов
function плохой_шаг2(вход: { x: number }): { условие: string } {
  return { условие: "готово" }; // конфликт с типом предиката
}

// ❌ Плохо — поля шаг, и, выход зарезервированы для fluent API
function плохой_шаг3(вход: { x: number }): { шаг: number, и: string, выход: boolean } {
  return { шаг: 1, и: "что-то", выход: true }; // конфликт с методами API
}

// ✅ Хорошо — используйте другие имена полей
function хороший_шаг(вход: { данные: string }): { результат: string } {
  return { результат: "готово" };
}

Лицензия

MIT