Skip to content

Repository files navigation

@deessejs/fp

Lightweight, type-safe functional programming utilities for TypeScript. Result, Maybe, Try, Unit, and friends — ESM-only, no runtime dependencies, designed for first-class interoperability with @deessejs/errors.

LicenseCIStarsnpm

Documentation

Sibling projects:@deessejs/errors provides error types that integrate natively with @deessejs/fp'sResult and Try. Install them together to get a complete error-handling story without glue code. Used internally at deessejs.com.


What's included

LayerWhat you getWhy it matters
Result<T, E>ok, err, pattern matching, sequencingType-safe error handling without exceptions or nulls.
Maybe<T>some, none, maybe, map, getOrElseOptional values that compose.
Try<T>try, tryAsync, conversion to ResultWrap throwing functions in a typed shell.
UnitThe unit type for void-returning operationsExpress "no value" without null or undefined.
Functional utilitiespipe, flow, identity, constant, flip, tupledCompose functions without ad-hoc helpers.
Async utilitiessleep, retry, timeout, QueueTime-based primitives that compose with Result.
Predicate utilitiesPredicate, Refinement, not, and, orFirst-class predicates and type guards.
Collection typesContext, Sequence, Collection, async iterator helpersSequence operations over various sources.
Generator compositiongen() with yield*Async flow control that reads like sync code.
@deessejs/errors integrationAll Result constructors accept @deessejs/errorsNo string-error footguns — use real error types.

Why this stack

  • Simple by default. No over-engineering, no fancy type gymnastics. Just the primitives you need to write cleaner code.
  • ESM-only. Modern packaging, no CJS shim, no module/main duplication.
  • Zero runtime dependencies. The only peer dep is @deessejs/errors, which is opt-in. The library itself is dependency-free.
  • TypeScript 6 first-class. Strict types, no any leakages, full inference. JSDoc where types alone are not enough.
  • Lockfile-clean pnpm workspaces. A single pnpm install rebuilds, lints, types, and tests the whole monorepo.
  • Real testing. Vitest, with coverage and integration tests against @deessejs/errors.

Quick start

Prerequisites

  • Node.js 22.14.0+ (engines.node enforced)
  • pnpm 10+ for development (corepack enable if not installed)
  • TypeScript 6+ for consumers (the package emits dist/*.d.ts)

Install

# Install @deessejs/fp with its optional sibling, @deessejs/errors.# See https://github.com/deessejs/errors
npm install @deessejs/fp @deessejs/errors

@deessejs/errors is optional — install it if you want Result<T, E> to carry typed errors instead of strings.

Usage

import{ok,err,some,none,maybe,pipe}from'@deessejs/fp';// Result: represent values that may have failedconstdivide=(a: number,b: number)=>b===0 ? err('Division by zero') : ok(a/b);constresult=divide(10,2);result.match({ok: (value)=>console.log(`Result: ${value}`),err: (error)=>console.error(`Error: ${error}`),});// Maybe: represent optional valuesconstuser={name: 'Alice',address: {city: 'Paris'}};constcity=maybe(user.address?.city).map((c)=>c.toUpperCase()).getOrElse('Unknown');// pipe: compose functions without glueconsttrim=(s: string)=>s.trim();constuppercase=(s: string)=>s.toUpperCase();constprocessed=pipe(' hello ',trim,uppercase);

Engine compatibility

RuntimeMinimum version
Node.js22.14.0
pnpm10 (for development)
TypeScript6.0

ESM-only. Consumers using a CJS resolver need to use dynamic import() or migrate to ESM.

Available commands

Workspace (root)

CommandWhat it does
pnpm buildBuild every workspace
pnpm testRun all tests in watch mode
pnpm test:runRun all tests once
pnpm lintLint every workspace
pnpm type-checkType-check every workspace
pnpm formatFormat with Prettier

Package: @deessejs/fp

CommandWhat it does
pnpm --filter @deessejs/fp buildBuild dist/
pnpm --filter @deessejs/fp testRun vitest in watch mode
pnpm --filter @deessejs/fp test:runRun vitest once
pnpm --filter @deessejs/fp type-checktsc --noEmit
pnpm --filter @deessejs/fp lintRun ESLint

App: web

CommandWhat it does
pnpm --filter web devStart the docs site in dev mode
pnpm --filter web buildBuild the docs site

Compatibility

Peer dependencies

PackageRequiredNotes
@deessejs/errorsOptional, peer >=1.0.0Required if you want err() to accept typed errors. Listed as a devDependency for testing.

Engines

FieldValue
engines.node>=22.14.0
packageManagerpnpm@10.30.3

Project structure

.
├── packages/
│ └── fp/ # The library — @deessejs/fp on npm
│ ├── src/ # Source code (ESM)
│ ├── dist/ # Build output (gitignored)
│ ├── vitest.config.ts
│ └── tsconfig.build.json
├── apps/
│ └── web/ # Documentation site (Next.js + Fumadocs)
├── docs/
│ ├── internal/ # Engineering plans, runbooks
│ │ ├── product/
│ │ └── versions/
│ └── CLAUDE.md # Claude / agent guidance
├── pnpm-workspace.yaml
├── turbo.json # Turborepo pipelines
├── .changeset/ # Changesets for versioning
└── README.md

Publishing

Releases are fully automated via Changesets + npm Trusted Publishing (OIDC). No long-lived NPM_TOKEN is required.

WhatHow
Bump versionAdd a .changeset/<topic>.md file with semver + description
Open the release PRchangesets-version.yml opens / updates a "Version Packages" PR from staging to main
PublishMerge the Version Packages PR → publish.yml runs → version bump committed → Trusted Publishing publishes to npm with provenance attestation
HotfixPush a tag vX.Y.Z to main → same workflow runs for the hotfix path
Rollback or deprecatePlanned: see docs/engineering/plans/release-pipeline-github-ui-setup.md

For the full pipeline design, see docs/engineering/plans/release-pipeline.md.

Architecture notes

  • ESM-only. The package exports ES modules. Consumers using legacy CJS resolvers must use dynamic import().
  • Strict types.Result.match requires both branches; Maybe.getOrElse requires a fallback. No partial type escapes.
  • Composition over inheritance. All primitives compose via pipe and flow. No class hierarchy, no extends.
  • Zero-runtime abstractions. No decorators, no reflection, no proxy traps. The library is straightforward to read in DevTools and node --prof.
  • Smoke-tested before publish. The release workflow runs a dynamic ESM import of the built artifact and verifies that key exports are present. A broken build fails the publish step before reaching npm.
  • @deessejs/errors is opt-in. The peer dep stays optional so consumers can adopt @deessejs/fp in isolation. Once @deessejs/errors is added, every err(error) call accepts a typed error.
  • One source of truth for auth-style errors. The apps/app/proxy.ts (in the broader deessejs monorepo, not in this repo) enforces email verification at the proxy level. @deessejs/errors errors are caught and translated to HTTP responses centrally.

Contributing

Open an issue to discuss larger changes. For typos, broken links, and small fixes, PRs are welcome.

Before submitting a PR:

  1. Run pnpm --filter @deessejs/fp test:run and pnpm --filter @deessejs/fp lint.
  2. Add a .changeset/<topic>.md if the change is user-facing (patch / minor / major).
  3. Update docs/internal/product/README.md if the API surface changes.

License

MIT. See the LICENSE file for details.

Support

About

Zero-dependency monads for bulletproof TypeScript applications

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages