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

@boring-stack-pkg/eslint-plugin-elysia

v0.1.2

Published

ESLint plugin enforcing architectural, type-safety, lifecycle, and performance patterns for Elysia.js applications.

Downloads

1,076

Readme

eslint-plugin-elysia

npm source

ESLint plugin enforcing architectural, type-safety, lifecycle, and performance patterns in Elysia.js applications.

Why

Elysia is a fluent, chain-based framework whose correctness depends on registration order, plugin naming, schema declaration, and idiomatic context destructuring. Most of those constraints aren't expressible in the type system — a missing schema, an unnamed plugin, a hook registered after a route, or a new Response() returned where Elysia would have serialized natively all type-check fine and silently misbehave at runtime.

This plugin pins those invariants down at lint time.

Install

pnpm add -D @boring-stack-pkg/eslint-plugin-elysia @typescript-eslint/parser

Usage (flat config)

// eslint.config.mjs
import tsParser from "@typescript-eslint/parser";
import elysia from "@boring-stack-pkg/eslint-plugin-elysia";

export default [
  {
    files: ["**/*.ts"],
    languageOptions: {
      parser: tsParser,
      parserOptions: { ecmaVersion: "latest", sourceType: "module" },
    },
    plugins: { elysia },
    rules: elysia.configs.recommended.rules,
  },
];

The recommended preset enables all twelve rules at "error". Override individual rules as needed:

rules: {
  ...elysia.configs.recommended.rules,
  "elysia/route-requires-tag": "warn",
  "elysia/prefer-static-services": "off",
  "elysia/no-direct-error-throw": [
    "error",
    { factoryName: "HttpErrors", factoryMethod: "make" }
  ]
}

Rules

| Rule | Category | Description | | ---------------------------------------------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------ | | route-requires-schema | Schema | Every route must declare at least one of body/query/params/response/headers/cookie. | | route-requires-tag | Convention | Every route must declare detail.tags for Swagger grouping. | | no-direct-error-throw | Safety (fixable) | Disallow throw new Error(...); use a typed error factory. | | consistent-status-via-set | Convention | Inside route handlers, set status via set.status = N, not new Response(body, { status }). | | prefer-destructured-context | Performance | Don't pass the full Elysia Context to controllers/services — destructure at the boundary. | | require-plugin-name | Lifecycle | Exported new Elysia(...) instances must declare { name: "..." } for runtime deduplication. | | no-separate-model-interfaces | Convention | Disallow TS interfaces that duplicate a runtime schema's shape; use typeof Schema.static (or equivalent). | | prefer-static-services | Performance | Don't new Service() inside route handlers when the class is stateless. | | require-hooks-before-routes | Lifecycle | Global hooks must register before any route on the same instance — Elysia's waterfall is order-sensitive. | | prefer-throw-status | Convention | Inside route handlers, prefer throw status(...) over try/catch building manual responses. | | prefer-direct-return | Performance | Return values directly; let Elysia serialize. Reserve new Response(...) for streams and custom headers. | | no-decorate-state-collision | Safety | Disallow duplicate keys across .decorate() / .state() / .derive() / .resolve() on a single instance. |

route-requires-schema

// ❌
new Elysia().post("/users", create);

// ✅
new Elysia().post("/users", create, {
  body: t.Object({ email: t.String() }),
  detail: { tags: ["Users"] },
});

require-plugin-name

// ❌
export const auth = new Elysia();

// ✅
export const auth = new Elysia({ name: "Auth.Plugin" });

require-hooks-before-routes

// ❌  onError will not fire for /health
new Elysia().get("/health", () => "ok").onError(handleError);

// ✅
new Elysia().onError(handleError).get("/health", () => "ok");

no-decorate-state-collision

// ❌  silent overwrite — second decorate("db", ...) wins
new Elysia({ name: "db" }).decorate("db", a).decorate("db", b);

// ✅
new Elysia({ name: "db" }).decorate("db", buildDb()).state("requestId", "");

For the full list of options and ❌/✅ snippets per rule, see docs/rules/.

Design notes

  • AST-only by design. No parserServices / type-checker dependency. Consumers don't need a parserOptions.project setup. Rules degrade gracefully where types could give more info — this is documented per-rule under "Limitations."
  • Family-aligned conventions. Mirrors eslint-plugin-module-boundaries and eslint-plugin-drizzle-conventions: same plugin export shape, same ESLintUtils.RuleCreator factory, same flat-config preset structure, same kebab-case rule ids.
  • Heuristic rules are explicit. Rules that can't be made precise without parser services (prefer-destructured-context, no-separate-model-interfaces, prefer-static-services, prefer-throw-status) document their limits in the rule docs.

Development

pnpm install
pnpm test
pnpm typecheck
pnpm build

Release

Tag a v* version locally and push the tag — .github/workflows/release.yml runs pnpm publish --access public with NPM_TOKEN.

License

MIT.