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

fluent-effect

v0.1.1

Published

An ergonomic TypeScript API over Effect.

Readme

fluent-effect

An ergonomic TypeScript API over Effect.

fluent-effect keeps native Effect available, but gives application code a smaller vocabulary for tasks, typed errors, dependencies, concurrency, and runtime entrypoints.

Install

bun add fluent-effect effect

effect is a peer dependency. Install it alongside fluent-effect so your application and this package share the same Effect runtime.

Quick Start

import { fx } from "fluent-effect";

const AppError = fx.errors<{
  NotFound: { id: string };
}>();

const loadUser = (id: string) =>
  fx.task(function* () {
    yield* fx.ensure(id.length > 0, () => AppError.NotFound({ id }));

    return yield* fx.require(id === "1" ? { id, name: "Ada" } : null, () =>
      AppError.NotFound({ id }),
    );
  });

const result = await fx.runResult(loadUser("1"));

if (result.ok) {
  console.log(result.value.name);
} else {
  console.error(result.error);
}

Imports

Use fluent-effect for the house API:

import { fx } from "fluent-effect";

Use fluent-effect/effect when you need native Effect modules:

import { Effect, Schedule, Duration } from "fluent-effect/effect";

Recommended API

User documentation lives in docs/. Start with Core Concepts, then use the focused behavior pages when you need exact semantics.

Create Tasks

fx.task(function* () {
  const value = yield* otherTask;
  return value;
});

fx.succeed(value);

fx.try({
  try: (signal) => fetch(url, { signal }),
  catch: (cause) => AppError.NetworkError({ cause }),
});

fx.try({
  try: () => JSON.parse(input),
  catch: (cause) => AppError.ParseError({ cause }),
});

fx.fail(AppError.NotFound({ id }));

fx.try is the general boundary for code that may throw or reject. It is async-safe even when the work is synchronous.

Typed Errors

See docs/errors.md for constructor discoverability, recovery helpers, and runtime boundary behavior.

Prefer fx.errors for application errors. Calling a constructor creates the error instance. Pass an explicit runtime spec when the available constructors must be discoverable with reflection APIs such as Object.keys, in, or object spread. Without that runtime spec, TypeScript-only error tags are erased at runtime and constructors remain lazy/direct-access only.

import type { ErrorOf, ErrorsOf } from "fluent-effect";

const AppError = fx.errors<{
  NotFound: { id: string };
  NetworkError: { cause: unknown };
}>({
  NotFound: null,
  NetworkError: null,
});

Object.keys(AppError); // ["NotFound", "NetworkError"]

type NotFound = ErrorOf<typeof AppError.NotFound>;
type AppErrors = ErrorsOf<typeof AppError>;

const loadUser = (id: string) =>
  fx.task(function* () {
    yield* fx.ensure(id.length > 0, () => AppError.NotFound({ id }));

    return yield* fx.require(id === "1" ? { id, name: "Ada" } : null, () =>
      AppError.NotFound({ id }),
    );
  });

const safeUser = fx.recoverErrors(loadUser("1"), {
  NotFound: () => fx.succeed(null),
});

Dependencies

See docs/dependencies.md for dependency tags, provider helpers, layer composition, and runtime wiring.

Define dependencies once, pull them by name inside tasks, provide implementations at the edge. Use provideDependency when you already have the dependency value, even if that value is an Effect. Use provideDependencyTask only when the dependency implementation must be built by running a Task.

import type { Task } from "fluent-effect";

interface Users {
  readonly findById: (id: string) => Task<User, NotFound>;
}

interface AuditLog {
  readonly record: (message: string) => Task<void>;
}

const Users = fx.dependency<Users>("Users");
const AuditLog = fx.dependency<AuditLog>("AuditLog");

const loadUser = fx.task(function* () {
  const { users, audit } = yield* fx.getDependency({
    users: Users,
    audit: AuditLog,
  });

  yield* audit.record("Loading user");
  return yield* users.findById("1");
});

const dependencies = fx.dependencies(
  fx.provideDependency(Users, {
    findById: () => fx.succeed({ id: "1", name: "Ada" }),
  }),
  // Build this implementation from a Task at startup.
  fx.provideDependencyTask(
    AuditLog,
    fx.succeed({
      record: (message) => fx.log(message),
    }),
  ),
);

const app = fx.app(dependencies);
const main = app.run(loadUser);

Concurrency

See docs/concurrency.md for sequential defaults, unbounded concurrency, bounded concurrency, and discard traversal behavior.

Use options when execution strategy matters. The default is sequential.

fx.sequence(tasks);
fx.sequence(tasks, { concurrency: true });
fx.sequence(tasks, { concurrency: 5 });

fx.each(items, fn);
fx.each(items, fn, { concurrency: true });
fx.each(items, fn, { concurrency: 5 });

fx.eachDiscard(items, fn);
fx.eachDiscard(items, fn, { concurrency: true });
fx.eachDiscard(items, fn, { concurrency: 5 });

Omit concurrency for sequential work, use true to turn parallelism on without a limit, or pass a number to bound parallelism. Use eachDiscard for fire-and-discard traversal over large collections when you need the effects but not the collected result array.

Retry, Timeout, Tracing

See docs/retry-timeout.md for retry attempt counting, backoff behavior, native schedules, and timeout failures.

fx.retry(task, { times: 3 });

fx.retry(task, {
  backoff: "100 millis",
  factor: 2,
  times: 5,
});

fx.timeout(task, "5 seconds", () => AppError.Timeout({ operation }));

fx.trace(task, "load-user", {
  attributes: { userId },
});

Runtime

See docs/runtime.md for choosing between Promise, throwing, result-object, exit, dependency-backed, and synchronous boundaries.

fx.run(task);
fx.runOrThrow(task);
fx.runResult(task);
fx.runWith(task, dependencies);

const app = fx.app(dependencies);
app.run(task);
app.runOrThrow(task);
app.runResult(task);
app.runExit(task);

Use runOrThrow at application boundaries when typed failures should be thrown as their original values. Use runResult when boundary code wants a plain JavaScript result object instead of native Effect Either or Exit values.

Native Escape Hatch

See docs/package-exports.md for package entrypoints and import guarantees.

fluent-effect/effect is a direct passthrough:

export * from "effect";

Use it when the house API does not cover what you need:

import { Effect, Schedule, Layer } from "fluent-effect/effect";

Examples

Focused examples live in examples/. A useful reading order is errors, dependencies, runtime, collections, then batch job.

examples/dependencies.ts
examples/errors.ts
examples/fallbacks.ts
examples/control-flow.ts
examples/composition.ts
examples/collections.ts
examples/batch-job.ts
examples/runtime.ts

See examples/README.md for what each example covers.

API Reference

See docs/api-reference.md for the exported fx helpers and public types.

Native-ish Aliases

These remain available for consistency and escape hatches, but examples prefer the clearer house names above.

Use the Sync variants only when you specifically need a synchronously runnable task or synchronous runtime execution.

fx.ok; // Effect.succeed
fx.sync; // Effect.sync
fx.fromSync; // Effect.sync
fx.trySync; // Effect.try, synchronously runnable throwing boundary
fx.chain; // Effect.flatMap
fx.tap; // Effect.tap
fx.tapError; // Effect.tapError
fx.span; // Effect.withSpan
fx.runSafe; // Effect.runPromiseExit
fx.runSync; // Effect.runSync
fx.runSafeSync; // Effect.runSyncExit
fx.runExitSync; // Effect.runSyncExit

fx.parallel; // fx.sequence with unbounded concurrency
fx.parallelLimit; // fx.sequence with bounded concurrency
fx.eachParallel; // fx.each with unbounded concurrency
fx.eachLimit; // fx.each with bounded concurrency
fx.eachDiscard; // fx.each with discarded results
fx.eachDiscardParallel; // fx.eachDiscard with unbounded concurrency
fx.eachDiscardLimit; // fx.eachDiscard with bounded concurrency

fx.layer; // Layer.effect
fx.layerSync; // Layer.succeed
fx.use; // Effect.provideService
fx.provide; // Effect.provide
fx.recoverTag; // Effect.catchTag
fx.recoverFrom; // Effect.catchTag
fx.timeoutFail; // Effect.timeoutFail
fx.retryTimes; // Effect.retry + Schedule.recurs
fx.retryBackoff; // Effect.retry + Schedule.exponential
fx.runOrThrow; // run and throw the original typed failure value
fx.runResult; // run and return a plain JavaScript Result value

Source Layout

src/index.ts
src/effect.ts
src/types.ts
src/builders.ts
src/concurrency.ts
src/errors.ts
src/dependencies.ts
src/logging.ts
src/runtime.ts

Contributing

This repository uses Bun for package management and validation.

bun install
bun run typecheck
bun run build