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

@opsydyn/oxlint-effect

v0.5.0

Published

Oxlint plugin rules for Effect TypeScript code shape constraints.

Readme

linteffect Oxlint plugin

Oxlint plugin rules for Effect TypeScript code-shape constraints.

Install

bun add -d oxlint @opsydyn/oxlint-effect

Configure

import { defineConfig } from "oxlint";
import { recommended } from "@opsydyn/oxlint-effect";

export default defineConfig({
  plugins: ["typescript"],
  jsPlugins: [...recommended.jsPlugins],
  rules: recommended.rules,
});

recommended.jsPlugins is exported as a readonly tuple. Spreading it creates the mutable array shape expected by Oxlint's ExternalPluginEntry[] config type.

Configure One Rule Group

Every documented rule group is exported as a config-shaped preset with the same shape as recommended. For example, teams that only want the DDD/domain modeling rules can use ddd:

import { defineConfig } from "oxlint";
import { ddd } from "@opsydyn/oxlint-effect";

export default defineConfig({
  plugins: ["typescript"],
  jsPlugins: [...ddd.jsPlugins],
  rules: ddd.rules,
});

Named group presets:

| Preset | Rule Group | | --- | --- | | reactAndRuntimeBoundaries | React and Runtime Boundaries | | effectComposition | Effect Composition | | concurrencySafety | Concurrency Safety | | pipelineShapeAndSequencing | Pipeline Shape and Sequencing | | branchingAndLocalControlFlow | Branching and Local Control Flow | | optionMatchAndDataNormalization | Option, Match, and Data Normalization | | atomStateAndPlatformBoundaries | Atom, State, and Platform Boundaries | | domainModeling | Domain Modeling | | ddd | Alias for domainModeling | | effectFlow | Effect Flow | | pureTransformation | Pure Transformation | | behaviorDecoration | Behavior Decoration | | styleSeparation | Style Separation | | serviceAndLayerArchitecture | Service and Layer Architecture |

Each preset also has a rule-only export with a Rules suffix. Use those when you want to compose multiple groups:

import { defineConfig } from "oxlint";
import {
  concurrencySafety,
  domainModelingRules,
  effectCompositionRules,
} from "@opsydyn/oxlint-effect";

export default defineConfig({
  plugins: ["typescript"],
  jsPlugins: [...concurrencySafety.jsPlugins],
  rules: {
    ...concurrencySafety.rules,
    ...domainModelingRules,
    ...effectCompositionRules,
  },
});

For local development inside this repository, point jsPlugins at the TypeScript source:

import { defineConfig } from "oxlint";
import { allRules } from "./src/index";

export default defineConfig({
  plugins: ["typescript"],
  jsPlugins: [{ name: "linteffect", specifier: "./src/index.ts" }],
  rules: allRules,
});

Rule Groups

The recommended config enables every rule as an error. The rules are heuristic: they flag code shapes that tend to hide Effect flow, domain meaning, or runtime boundaries.

React and Runtime Boundaries

| Rule | Catches | Why | | --- | --- | --- | | linteffect/no-react-state | React state hooks such as useState, useReducer, and useEffect. | Keeps React UI state in the atom/runtime model instead of bypassing it. | | linteffect/no-runtime-runfork | Runtime.runFork(...). | Detached fibers hide ownership, interruption, and lifecycle boundaries. | | linteffect/no-run-effect-outside-boundary | Direct Effect.runPromise, Effect.runSync, Effect.runFork, and related Effect.run* calls. | Keeps Effect execution owned by app, CLI, worker, route, or test boundaries. | | linteffect/no-or-die-outside-boundary | Effect.orDie(...), Effect.orDieWith(...), and pipe arguments such as Effect.orDie. | Prevents recoverable typed failures from being converted to defects inside domain logic. | | linteffect/prevent-dynamic-imports | Dynamic import(...). | Static imports keep dependency boundaries visible. | | linteffect/no-render-side-effects | Match.value(...).pipe(...) used as a render-time statement. | Prevents side effects from running during render. | | linteffect/no-inline-runtime-provide | Inline Effect.provide(...) inside local runtime/generator chains. | Keeps dependency assembly at service or application boundaries. |

Effect Composition

| Rule | Catches | Why | | --- | --- | --- | | linteffect/no-effect-as | Direct Effect.as(...) wrappers. | Makes value flow explicit instead of discarding meaning behind a placeholder. | | linteffect/no-effect-do | Effect.Do. | Avoids builder-style hidden sequencing. | | linteffect/no-effect-bind | Effect.bind(...). | Prefers direct Effect.gen or pipeline flow over builder state. | | linteffect/no-effect-async | Effect.async(...). | Manual callback bridges are easy to leak or resume incorrectly. | | linteffect/no-effect-ignore | Effect.ignore(...) and pipe arguments such as Effect.ignore. | Makes ignored failures explicit at boundaries instead of burying failure ownership. | | linteffect/no-effect-never | Effect.never. | Infinite effects should have explicit lifecycle and teardown ownership. | | linteffect/no-effect-fn-generator | Effect.fn(function* ...). | Avoids wrapper generators that obscure sequencing. | | linteffect/no-nested-effect-gen | Effect.gen nested inside another Effect.gen. | Keeps generator-based effects linear. | | linteffect/no-yield-without-star-in-effect-gen | Plain yield inside Effect.gen. | Requires yield* so generator steps delegate to the Effect interpreter. | | linteffect/no-async-effect-combinator-callback | async callbacks passed to common Effect combinators. | Prevents Promise-returning callbacks from bypassing Effect error, interruption, and tracing semantics. | | linteffect/no-throw-in-effect-logic | throw inside Effect.gen or common Effect combinator callbacks. | Keeps failures in typed Effect error channels. | | linteffect/no-try-catch-in-effect-logic | try/catch inside Effect.gen or common Effect combinator callbacks. | Uses Effect error combinators instead of local imperative recovery. | | linteffect/no-promise-api-in-effect-logic | Promise.all, Promise.race, .then, .catch, and related Promise APIs inside Effect logic. | Keeps scheduling, cancellation, tracing, and failures inside Effect. | | linteffect/no-swallowed-catch-all | Effect.catchAll handlers that recover with Effect.succeed, Effect.void, Effect.asVoid, or Effect.ignore. | Avoids hiding failures without telemetry, re-fail, or explicit typed recovery. | | linteffect/no-manual-effect-channels | Manual Effect.Effect<...> and Layer.Layer<...> channel types. | Lets Effect infer channels from real composition. | | linteffect/no-effect-type-alias | Type aliases around Effect.Effect<...>. | Keeps service surfaces concrete and discoverable. | | linteffect/no-public-generic-effect-error | Exported APIs returning Effect.Effect<_, Error, _>. | Public Effect APIs should expose tagged, recoverable domain errors instead of generic Error. |

Effect Flow

| Rule | Catches | Why | | --- | --- | --- | | linteffect/no-piped-yield-in-gen | Two or more yield* effect.pipe(...) steps inside one Effect.gen. | Keeps decorated effects named before the workflow so generator bodies read as a clear story. | | linteffect/no-gen-for-mapping | Tiny Effect.gen blocks that yield once and return a pure transform. | Simple value mapping belongs in Effect.map or a named pure transformation, not workflow syntax. | | linteffect/prefer-gen-for-workflow | Pipelines with three or more sequencing combinators such as Effect.flatMap, Effect.andThen, Effect.tap, or Effect.zipRight. | Long sequencing pipelines read like imperative workflow; Effect.gen makes the happy path explicit. |

Pure Transformation

| Rule | Catches | Why | | --- | --- | --- | | linteffect/no-large-anonymous-flow | flow(...) expressions with five or more transformation steps. | Large pure pipelines need a domain name so the transformation is reusable and reviewable. | | linteffect/no-effect-in-flow | Effect.*, yield, await, async callbacks, console calls, Promise, or runtime access inside flow(...). | flow() should stay pure; effectful workflow, retries, logging, and dependency access belong in Effect code. | | linteffect/prefer-named-flow | Non-trivial flow(...) expressions passed inline as callback/combinator arguments. | Naming the transformation makes DTO mapping and business calculations explicit instead of anonymous callback logic. |

Behavior Decoration

| Rule | Catches | Why | | --- | --- | --- | | linteffect/prefer-pipe-for-behavior | Static behavior decorators such as Effect.retry(Effect.succeed(...), policy). | Retry, timeout, spans, logging, recovery, DI, and value transforms should read as behavior around an existing effect. | | linteffect/prefer-decorated-effect-before-gen | Two or more decorated yield* service.pipe(Effect.retry(...)) / yield* effect.pipe(Effect.withSpan(...)) steps inside one Effect.gen. | Generator bodies should tell the workflow story; behavior policy should be named before the workflow. | | linteffect/no-workflow-in-behavior-pipe | Pipes that mix behavior decorators with multiple workflow sequencing operators or embedded control flow. | .pipe() should answer how an effect behaves, not bury multi-step workflow that belongs in Effect.gen. |

Style Separation

| Rule | Catches | Why | | --- | --- | --- | | linteffect/no-mixed-pillar-function | Functions that mix three or more style pillars: workflow, pure transformation, behavior decoration, and Layer construction. | Each function should have one obvious style so the domain story, policies, transformations, and wiring stay separately reviewable. | | linteffect/no-clever-effect-expression | Deep or wrapper-heavy expressions combining multiple style pillars, such as pipe(Effect.map(Effect.gen(...), flow(...)), ((x) => x)). | Dense expression towers hide intent and make Effect code harder to debug or refactor. | | linteffect/prefer-extracted-concept | Multi-statement anonymous callbacks passed into Effect combinators. | Inline callback bodies with several steps usually represent a named transformation, policy, or workflow concept. |

Service and Layer Architecture

| Rule | Catches | Why | | --- | --- | --- | | linteffect/prefer-effect-service | Context.Tag(...) and Context.GenericTag(...) service definitions. | Modern Effect.Service gives services generated accessors, consistent default layers, and clearer dependency ownership. | | linteffect/no-layer-provide-in-service-definition | Layer.provide(...) nested inside an Effect.Service options object. | Service definitions should declare implementation and dependencies; layer assembly belongs at application, test, or composition boundaries. | | linteffect/require-service-accessors | Effect.Service classes whose options omit accessors: true. | Static accessors keep service APIs consistent and avoid hand-written dependency plumbing. | | linteffect/require-service-dependencies | Effect.Service implementations that yield* SomeService without a dependencies option. | Service dependency graphs should be declared where the service is defined. | | linteffect/no-namespace-effect-import | import * as ... from "effect" and other Effect package namespace imports. | Direct named imports keep the Effect surface explicit and easier to scan. | | linteffect/no-manual-service-object-export | Exported *Service object literals with function-valued members. | Public service APIs should use Effect.Service for accessors, default layers, and dependency ownership. | | linteffect/no-layer-merge-in-request-handler | Layer.merge* or Layer.provide inside request/route/handler functions. | Request handlers should run programs, not assemble the application dependency graph. | | linteffect/no-service-method-returning-promise | Methods returned from Effect.Service implementations that return Promise. | Service APIs should preserve Effect cancellation, tracing, typed failures, and dependency semantics. | | linteffect/prefer-layer-pipe | Nested Layer.provide(...) call towers. | Layer assembly should read as a left-to-right composition pipeline. | | linteffect/no-inline-layer-provide-in-program | Effect.provide(...) or Layer.provide(...) buried inside Effect.gen program bodies. | Programs should describe workflow; application layer provisioning belongs at composition boundaries. | | linteffect/prefer-layer-mergeall-for-infrastructure | Nested Layer.merge(...) chains. | Infrastructure groups should use Layer.mergeAll(...) so dependency groups stay visible. | | linteffect/no-service-layer-scatter | Three or more separate *Layer/*Live constants with inline Layer.provide or Effect.provide. | Service and infrastructure layers should be grouped by concern instead of scattered one constant at a time. |

Concurrency Safety

| Rule | Catches | Why | | --- | --- | --- | | linteffect/no-unbounded-effect-all | Effect.all(items.map(...)) without an explicit concurrency option. | Prevents load-dependent runaway parallelism and makes throughput ownership explicit. | | linteffect/no-fire-and-forget-fork | Bare Effect.fork(...) expression statements. | Detached fibers hide failure, interruption, and lifecycle ownership. | | linteffect/no-fork-in-loop | Effect.fork(...) inside for, for...of, for...in, while, or do...while loops. | Avoids loop-spawned unbounded fibers; use bounded Effect.all / Effect.forEach or scoped supervision. | | linteffect/no-race-without-cleanup | Effect.race(...) / Effect.raceAll(...) without Effect.ensuring, scoped, or acquire/release cleanup. | Racing effects need explicit loser cleanup so losing work and resources do not leak. | | linteffect/no-unobserved-fiber | const fiber = Effect.fork(...) when the fiber is never passed to Fiber.join, Fiber.await, or Fiber.interrupt. | Forked fibers should have observed failure and interruption ownership. | | linteffect/no-unbounded-concurrent-retry | Effect.retry(...) nested inside unbounded mapped Effect.all(...) or unbounded Effect.forEach(...). | Prevents retry storms by requiring bounded concurrency or a queue/backoff policy. | | linteffect/no-blocking-call-in-effect | Sync fs / crypto / zlib calls inside Effect.sync or Effect.gen. | Blocking calls stall the runtime worker and should live behind async/platform boundaries. | | linteffect/no-promise-concurrency-in-effect | Promise.all, Promise.allSettled, Promise.race, or Promise.any inside Effect logic. | Keeps concurrency, interruption, tracing, and typed failures inside Effect. | | linteffect/no-shared-mutable-state-across-fibers | Outer let / var state mutated from Effect.fork, Effect.all, or Effect.forEach work. | Shared mutable state across fibers creates nondeterministic races; use Effect concurrency primitives. | | linteffect/no-timeout-with-noninterruptible-promise | Effect.timeout(Effect.promise(...)) or Effect.timeout(Effect.tryPromise(...)) without a signal-aware callback. | Timeout should interrupt the underlying async operation, not only the Effect wrapper. | | linteffect/no-uninterruptible-concurrent-region | Effect.uninterruptible(...) wrapping fork, race, all, forEach, or queue-taking work. | Broad uninterruptible concurrent regions block cancellation and can strand work during shutdown. | | linteffect/no-unbounded-queue-or-pubsub | Queue.unbounded() and PubSub.unbounded(). | Unbounded buffers hide backpressure and can fail under load; capacity should be owned explicitly. | | linteffect/no-global-mutable-concurrency-state | Module-level mutable state or mutable containers touched from concurrent Effect work. | Global mutable state under concurrency behaves like shared memory; move ownership into Effect primitives or layers. |

Pipeline Shape and Sequencing

| Rule | Catches | Why | | --- | --- | --- | | linteffect/no-nested-effect-call | Deeply nested Effect.xx(Effect.yy(...)) calls. | Flattens sequencing into readable pipelines. | | linteffect/no-effect-ladder | Nested Effect combinator ladders in assignments or returns. | Avoids control flow hidden inside expression towers. | | linteffect/no-flatmap-ladder | Nested Effect.flatMap and map plus flatten ladders. | Encourages one clear bind point after context is built. | | linteffect/no-pipe-ladder | Nested pipe(...) or method .pipe(...) chains. | Keeps pipelines flat and scan-friendly. | | linteffect/no-call-tower | Effect calls passed directly into other Effect calls. | Makes intermediate effects named or piped. | | linteffect/no-effect-orElse-ladder | Effect.orElse wrapped around sequencing chains. | Keeps error handling at an explicit decision point. | | linteffect/no-effect-wrapper-alias | Const/function aliases that only wrap Effect calls. | Discourages wrapper choreography with no domain meaning. | | linteffect/warn-effect-sync-wrapper | Effect.sync(() => someCall()) around non-console calls. | Avoids hiding side effects behind vague sync wrappers. | | linteffect/no-effect-side-effect-wrapper | Effect.as or Effect.zipRight around side-effecting operands. | Prevents side effects from being disguised as discarded values. | | linteffect/no-effect-all-step-sequencing | Sequential side effects hidden in Effect.all(..., { concurrency: 1 }). | Reserves Effect.all for aggregation, not imperative step lists. | | linteffect/no-effect-succeed-variable | Effect.succeed(variable) used as a branch placeholder. | Encourages selecting plain values before entering Effect flow. |

Branching and Local Control Flow

| Rule | Catches | Why | | --- | --- | --- | | linteffect/no-if-statement | Imperative if statements in Effect files. | Pushes branching toward typed Match/Option/Either decisions. | | linteffect/no-switch-statement | Imperative switch statements in Effect files. | Encourages exhaustive domain matching. | | linteffect/no-ternary | Ternary expressions in Effect files. | Keeps decisions explicit and named. | | linteffect/no-try-catch | try/catch. | Keeps failures in typed Effect error channels. | | linteffect/no-arrow-ladder | Nested arrow IIFEs. | Avoids local wrapper control flow. | | linteffect/no-iife-wrapper | Immediately invoked function wrappers. | Moves decisions into named values or pipelines. | | linteffect/no-return-in-arrow | return inside block-bodied arrow callbacks. | Prefers expression callbacks for simple pipeline steps. | | linteffect/no-return-in-callback | return inside inline function callbacks. | Reduces hidden local control flow. | | linteffect/no-return-null | return null in Effect files. | Uses Option.none or typed failures instead of null sentinels. | | linteffect/no-branch-in-object | Match/Option/Either decisions inside object literals. | Computes decisions first, then builds data from named values. |

Option, Match, and Data Normalization

| Rule | Catches | Why | | --- | --- | --- | | linteffect/no-option-as | Option.as(...). | Makes selection explicit with Option.map or Option.match. | | linteffect/no-match-void-branch | Match branches returning Effect.void. | Avoids no-op branches that hide guard-style control flow. | | linteffect/no-match-effect-branch | Multi-step sequencing inside Match or Option branches. | Selects data in Match/Option, then runs one Effect pipeline. | | linteffect/no-model-overlay-cast | as assertions on decoded model flow. | Avoids hiding schema drift with unchecked overlays. | | linteffect/no-unknown-boolean-coercion-helper | Local unknown-to-boolean checks paired with null fallback matching. | Moves boolean normalization to the schema boundary. | | linteffect/no-fromnullable-nullish-coalesce | Option.fromNullable(value ?? null) or ?? undefined. | Passes nullable sources directly without rewrapping noise. | | linteffect/no-option-boolean-normalization | Repeated Option.match boolean normalization. | Normalizes once at the boundary and reads typed booleans later. | | linteffect/no-string-sentinel-return | Effect.succeed("token") sentinel returns. | Uses domain values, Option/Either, or tagged unions for decisions. | | linteffect/no-string-sentinel-const | String constants used as state/status tokens. | Avoids ad hoc string state machines. |

Atom, State, and Platform Boundaries

| Rule | Catches | Why | | --- | --- | --- | | linteffect/no-effect-sync-console | console.* inside Effect.sync. | Uses Effect.log* or a real logging boundary. | | linteffect/no-atom-registry-effect-sync | Atom or atom registry operations wrapped in Effect.sync. | Keeps atom operations in the atom flow. | | linteffect/no-family-collection-read | Atom.family projections that read broad collection atoms. | Keeps keyed atoms keyed instead of coupling to whole collections. | | linteffect/no-naked-object-state-update | Raw object spreading, Object.assign, JSON rebuilds, and similar state shortcuts. | Preserves explicit model transitions and schema boundaries. | | linteffect/no-wrapgraphql-catchall | Effect.catchAll after wrapGraphqlCall or applyResponse. | Handles GraphQL envelope errors at the response mapping boundary. |

Domain Modeling

| Rule | Catches | Why | | --- | --- | --- | | linteffect/no-raw-domain-id-alias | type UserId = string and similar raw ID aliases. | Branded IDs prevent swapped identifiers across boundaries. | | linteffect/no-boolean-domain-flag | Boolean behavior flags such as shouldNotifyCustomer. | Replaces hidden modes with commands, tagged unions, or explicit functions. | | linteffect/no-magic-domain-string | Raw string comparisons such as status === "approved". | Makes domain vocabularies typed and exhaustive. | | linteffect/no-raw-domain-primitive-params | Domain-looking functions with several raw string/number params. | Introduces branded values or command objects for meaningful inputs. | | linteffect/no-raw-time-domain-field | Time-looking fields typed as number or Date. | Models durations and clock boundaries explicitly. | | linteffect/no-overloaded-options-object | opts, options, or config typed as any or object. | Uses Schema decoding or named config models instead of loose bags. | | linteffect/no-domain-logic-in-conditional | Multi-clause business rules embedded in boolean expressions. | Extracts named predicates or validation Effects that can be tested. | | linteffect/no-implicit-state-machine-object | Multiple boolean lifecycle flags on one object. | Models impossible states away with tagged unions. | | linteffect/no-adhoc-domain-error | Effect.fail("...") and throw new Error("...") in domain code. | Uses structured tagged errors for recovery and observability. | | linteffect/no-domain-meaning-by-folder-only | Admin/public/internal meaning encoded only in names around raw IDs. | Represents context in types, commands, policies, or services. |