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

effect-rules

v0.3.6

Published

Custom oxlint rules for Effect TypeScript projects.

Readme

effect-rules

Oxlint rules for Effect TypeScript projects that turn team conventions into fast, local feedback.

These rules focus on the Effect mistakes that usually survive typechecking: untyped error channels, unsafe boundary parsing, hidden dependency gaps, test runners that bypass Effect, and v4 APIs that no longer exist.

Opinionated: this is what I think is best for Effect codebases. I might be wrong.

Why Use This

| Benefit | What the rules catch | Example rules | | -------------------------------------- | --------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | | Keep failures typed | Raw throws, generic Error, Promise.reject, swallowed failures, die / orDie escape hatches | no-raw-throw, no-built-in-error-constructor, no-promise-reject, no-effect-ignore | | Make unsafe boundaries explicit | any, casts, non-null assertions, unchecked JSON, ad hoc shape probing, skipped validation | no-explicit-any, no-type-casting, no-json-parse, no-schema-error-response-leak | | Protect Effect architecture | Optional services, nested layer wiring, direct fetch, manual service tags, unvalidated SQL decoding | no-service-option, prefer-context-service, no-nested-layer-provide, no-direct-fetch | | Standardize Effect style | Yieldable errors, Effect.fn, Match decision tables, constructors for tagged values | prefer-yieldable-error, prefer-effect-fn, prefer-match-validation, prefer-tagged-constructor | | Make tests run like production Effects | Direct vitest imports, manual runners, manual layer builds, expect instead of Effect assertions | prefer-effect-vitest, no-effect-run-in-tests, no-vitest-import, prefer-effect-vitest-assert | | Stay on supported Effect v4 APIs | Removed or renamed APIs such as Effect.async, zipRight, timeoutFail, catchIf | no-unsupported-effect-api |

Install

bun add -d effect-rules oxlint

Usage

For app code, prefer oxlint.config.ts and import a preset.

import { defineConfig } from "oxlint";
import { strict } from "effect-rules/configs";

export default defineConfig({
  ...strict,
  ignorePatterns: ["**/dist/**", "**/node_modules/**", ".repos/**"],
});

Use recommended for the smaller high-signal starter set.

import { defineConfig } from "oxlint";
import { recommended } from "effect-rules/configs";

export default defineConfig({
  ...recommended,
  ignorePatterns: ["**/dist/**", "**/node_modules/**", ".repos/**"],
});

If you need .oxlintrc.json, oxlint currently resolves extends as a path, so use the package file path.

{
  "$schema": "./node_modules/oxlint/configuration_schema.json",
  "extends": ["./node_modules/effect-rules/configs/strict.json"],
  "ignorePatterns": ["**/dist/**", "**/node_modules/**", ".repos/**"]
}

JSON starter set:

{
  "$schema": "./node_modules/oxlint/configuration_schema.json",
  "extends": ["./node_modules/effect-rules/configs/recommended.json"],
  "ignorePatterns": ["**/dist/**", "**/node_modules/**", ".repos/**"]
}

Or add the plugin manually and enable only the rules you want. Published versions 0.3.5 and newer can be loaded directly by Oxlint's JS plugin resolver without a local shim.

{
  "jsPlugins": ["effect-rules"],
  "rules": {
    "effect/no-explicit-any": "error",
    "effect/no-type-casting": "error",
    "effect/no-non-null-assertion": "error",
    "effect/no-raw-throw": "error",
    "effect/prefer-yieldable-error": "warn",
    "effect/prefer-effect-fn": "warn"
  }
}

For local development before publishing:

{
  "jsPlugins": ["./node_modules/effect-rules/src/index.ts"]
}

If Oxlint reports Failed to load JS plugin: effect-rules, make sure you are on [email protected] or newer.

Recommended Starting Set

Start with the rules that prevent the highest-cost bugs first.

{
  "rules": {
    "effect/no-explicit-any": "error",
    "effect/no-type-casting": "error",
    "effect/no-non-null-assertion": "error",
    "effect/no-ts-nocheck": "error",
    "effect/no-disable-validation": "error",
    "effect/no-raw-throw": "error",
    "effect/no-built-in-error-constructor": "error",
    "effect/no-effect-fail-new-error": "error",
    "effect/no-promise-reject": "error",
    "effect/no-promise-catch": "error",
    "effect/no-effect-ignore": "error",
    "effect/no-silent-error-swallow": "error",
    "effect/no-json-parse": "error",
    "effect/no-schema-error-response-leak": "error",
    "effect/no-unknown-shape-probing": "error",
    "effect/no-unsupported-effect-api": "error",
    "effect/no-unknown-runtime-requirements": "error",
    "effect/no-bare-yield-in-effect-generator": "error",
    "effect/no-floating-effect-in-generator": "error",
    "effect/no-return-effect-from-generator": "error",
    "effect/no-effect-run-in-effect-code": "error"
  }
}

Then add style and architecture rules as teams converge on the conventions.

Rule Groups

Type Safety

| Rule | Benefit | | ---------------------------------------- | ----------------------------------------------------------------- | | effect/no-explicit-any | Keeps unknown data honest until it is narrowed or decoded. | | effect/no-type-casting | Prevents papering over mismatched types with assertions. | | effect/no-non-null-assertion | Makes absence explicit with Option or validation. | | effect/no-ts-nocheck | Stops entire files from opting out of type safety. | | effect/no-disable-validation | Keeps schemas as runtime contracts, not documentation. | | effect/no-sql-type-parameter | Pushes SQL results through Schema-backed decoding. | | effect/no-unknown-runtime-requirements | Keeps runtime runners tied to their concrete service context. | | effect/no-process-env | Reads environment through Effect Config instead of globals. | | effect/prefer-option-from-nullable | Replaces hand-written null checks with one canonical constructor. |

Effect Error Handling

| Rule | Benefit | | ----------------------------------------- | -------------------------------------------------------------------- | | effect/no-raw-throw | Keeps domain failures in the Effect error channel. | | effect/no-try-catch | Moves exception capture into typed Effect constructors and handlers. | | effect/no-built-in-error-constructor | Avoids opaque Error values in domain code. | | effect/no-effect-fail-new-error | Requires typed failures instead of Effect.fail(new Error(...)). | | effect/prefer-schema-tagged-error-class | Standardizes domain errors on Schema.TaggedErrorClass. | | effect/prefer-yieldable-error | Removes noisy Effect.fail(new DomainError(...)) in generators. | | effect/prefer-yieldable-error-in-match | Keeps Match handlers returning domain errors, not nested Effects. | | effect/no-effect-ignore | Forces explicit recovery or propagation. | | effect/no-effect-catchallcause | Avoids catching defects as recoverable errors. | | effect/no-effect-escape-hatch | Preserves typed failures instead of collapsing them into defects. | | effect/no-silent-error-swallow | Catches catch* handlers that erase failures with Effect.void. | | effect/prefer-map-error-for-wrapping | Uses Effect.mapError for handlers that only wrap and re-fail. | | effect/no-instanceof-error | Encourages tagged-error handling instead of runtime class checks. | | effect/no-unknown-error-message | Stops lossy unknown-error stringification. | | effect/no-promise-catch | Models async failures with Effect instead of Promise chains. | | effect/no-promise-reject | Keeps failures in Effect constructors and Effect.tryPromise. |

Effect Architecture

| Rule | Benefit | | ----------------------------------------------- | ------------------------------------------------------------------------------- | | effect/prefer-context-service | Prefers Context.Service for service APIs while leaving deliberate keys alone. | | effect/prefer-inline-context-service-shape | Keeps service shape, config input, and layer construction local. | | effect/no-service-option | Makes required dependencies impossible to silently omit. | | effect/no-primitive-context-service | Keeps service APIs object-shaped instead of primitive values. | | effect/no-promise-service-method | Keeps service methods in Effect instead of leaking Promise. | | effect/no-nested-layer-provide | Keeps layer graphs flatter and easier to inspect. | | effect/prefer-layer-provide-merge | Replaces manual provide-then-merge wiring with explicit intent. | | effect/no-repeated-layer-factory | Encourages binding layer factories once before composition. | | effect/prefer-layer-constructor-semantics | Uses succeed, sync, or effect to match construction semantics. | | effect/prefer-fork-scoped-in-layer | Ties fibers started during layer construction to the layer scope. | | effect/prefer-static-effect | Avoids zero-argument thunks around already-lazy Effects. | | effect/prefer-stream-from-pubsub | Exposes PubSub events as scoped Streams instead of raw subscriptions. | | effect/prefer-service-log-annotations | Ensures service constructor logs carry service metadata. | | effect/no-unnecessary-effect-tx | Reserves Effect.tx for real STM transaction boundaries. | | effect/no-direct-fetch | Pushes HTTP through typed Effect clients or adapters. | | effect/no-native-clock-in-effect | Replaces native current-time reads with Clock / DateTime. | | effect/no-native-random-in-effect | Replaces native randomness with Effect Random. | | effect/no-timer-api-in-effect | Replaces native timers with Effect.sleep, schedules, or scoped work. | | effect/no-promise-combinators-in-effect | Replaces Promise.all / races with Effect concurrency APIs. | | effect/no-inline-schema-compile | Hoists Schema compilation so hot paths do not rebuild decoders. | | effect/no-localstorage | Blocks fragile auth or secret state in browser storage. | | effect/no-raw-indexeddb | Uses schema-backed platform IndexedDb instead of raw browser APIs. | | effect/no-schema-error-response-leak | Logs decode failures without exposing SchemaError details to clients. | | effect/prefer-shared-managed-runtime | Keeps layer-provided JS boundaries on shared managed runtimes. | | effect/require-callback-cleanup-for-listeners | Prevents callback listener/resource leaks on interruption. | | effect/prefer-scoped-temp-cleanup | Uses scoped temp resources instead of manual cleanup. | | effect/no-nested-semaphore-acquire | Avoids deadlock-prone nested semaphore acquisition. |

Data Modeling And Control Flow

| Rule | Benefit | | ---------------------------------------- | ------------------------------------------------------------------- | | effect/prefer-tagged-constructor | Centralizes tagged value creation behind constructors. | | effect/prefer-data-tagged-enum | Makes tagged unions consistent and constructor-backed. | | effect/no-manual-tag-check | Replaces brittle _tag probing with Effect and Predicate helpers. | | effect/prefer-match-validation | Turns validation ladders into readable decision tables. | | effect/prefer-match-value | Turns return-only string switches into exhaustive Match mappings. | | effect/no-void-expression | Prevents accidental value discard through void. | | effect/no-json-parse | Requires Schema decoding for JSON boundaries. | | effect/no-sync-schema-decode-in-effect | Uses Effect-based Schema decoders inside Effect workflows. | | effect/no-unknown-shape-probing | Replaces ad hoc probing with Schema or named typed guards. |

Effect Function Style

| Rule | Benefit | | ------------------------------------------ | --------------------------------------------------------------------------- | | effect/prefer-effect-fn | Names reusable Effect functions and improves traces. | | effect/no-bare-yield-in-effect-generator | Requires yield* inside Effect generators. | | effect/no-floating-effect-in-generator | Catches missed yield* / composition for Effect calls in generators. | | effect/no-return-effect-from-generator | Prevents accidental Effect<Effect<...>> from generator returns. | | effect/no-effect-run-in-effect-code | Keeps Effect.run* at runtime boundaries instead of inside Effect code. | | effect/no-effect-fn-immediate-invocation | Avoids Effect.fn(...)(...)() wrappers by putting params on the generator. | | effect/no-as-effect-method-reference | Avoids unbound .asEffect references in Match handlers. | | effect/no-unsupported-effect-api | Catches APIs unavailable in Effect v4 / effect-smol. |

Effect Tests

| Rule | Benefit | | --------------------------------------- | ---------------------------------------------------------------------- | | effect/no-vitest-import | Keeps test helpers on @effect/vitest. | | effect/prefer-effect-vitest | Uses it.effect(...) for Effect tests while leaving pure tests alone. | | effect/prefer-effect-vitest-assert | Uses Effect-aware assert instead of expect. | | effect/no-effect-run-in-tests | Avoids manually running Effects in test bodies. | | effect/no-manual-layer-build-in-tests | Uses test layers through Effect test helpers instead of manual builds. |

Examples

Typed Failures

// flagged
yield * Effect.fail(new DomainError({ message }));

// preferred
yield * new DomainError({ message });

Tagged Constructors

// flagged
const state = { _tag: "Idle" };

// preferred
const State = Data.taggedEnum<State>();
const state = State.Idle();

Tagged Unions

// flagged
export type AuthHttpError =
  | { readonly _tag: "Unauthorized" }
  | { readonly _tag: "BadRequest"; readonly reason: string };

// preferred
export type AuthHttpError = Data.TaggedEnum<{
  Unauthorized: {};
  BadRequest: { readonly reason: string };
}>;

export const AuthHttpError = Data.taggedEnum<AuthHttpError>();

Match Validation

// flagged
if (input.name.length === 0) {
  return Effect.fail(new EmptyNameError());
}

// preferred
const validate = Match.type<Input>().pipe(
  Match.when({ name: "" }, () => new EmptyNameError()),
  Match.orElse(() => Effect.void),
);

Development

bun install
bun test
bun run check

bun run check builds the package, smoke-tests that Node can import dist/index.js, and verifies Oxlint can load jsPlugins: ["effect-rules"] from the built package. Keep relative ESM imports fully specified in source files, for example ./rules.js rather than ./rules, so the emitted package works with Node's ESM resolver.