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

@avelonjs/bailiff

v0.8.1

Published

ESLint plugin that enforces Avelon architecture rules.

Readme

@avelonjs/bailiff

@avelonjs/bailiff is the architecture linter. It is an ESLint plugin plus a CLI wrapper so violations appear as editor squiggles and as reeve bailiff in CI. Reach for it when you want Avelon's directory map, driver boundary, and generated-file rule enforced rather than requested in a contributing guide.

A template is advice. A linter is a decision.

Installation

bun add -d @avelonjs/bailiff eslint

Point ESLint at the recommended config. Every rule is on by default; you may downgrade one in avelon.config.ts, and reeve doctor prints the drift.

import bailiff from '@avelonjs/bailiff'

export default [bailiff.configs.recommended]

Basic Usage

import { runBailiff, explain } from '@avelonjs/bailiff'

explain('no-orphan-query')

const result = await runBailiff({ cwd: process.cwd(), fix: false })
if (result.exitCode !== 0) {
  throw new Error(result.output)
}
reeve bailiff
reeve bailiff --fix
reeve bailiff --explain no-orphan-query

--fix applies autofixes only. --explain prints the one-sentence reason for a rule and exits. A non-TTY stdout never opens a prompt.

A default scan of . skips node_modules, .next, dist, archive/, and spikes/. Archive and throwaway spikes are not the product linter corpus. @avelonjs/core and @avelonjs/orm tests may import bun:test; runtime sources in those packages still may not import bun:*.

Rules

Every rule ships with a documented reason and an autofix or a disable-with-reason escape hatch. A bare // bailiff-disable-next-line with no reason is itself an error.

// bailiff-disable-next-line no-raw-outside-drivers -- pgvector similarity, no IR support yet
const rows = await DB.raw().rpc('match_documents', { embedding })

| Rule | Enforces | Level | | -------------------------- | ------------------------------------------------------------------------------------------------ | ----- | | no-vendor-import | No vendor SDK imported outside a driver package or avelon.config.ts | error | | no-cross-layer | Views cannot import Models. Controllers cannot import Controllers. Models cannot import Http/. | error | | no-orphan-query | Database access only in Models, Actions, Errands, and seeds | error | | no-unwarded | Scrivener.unwarded() only in app/Errands/ and database/seeds/ | error | | require-ward | Every model has a ward | error | | no-raw-outside-drivers | driver.raw() only in app/Drivers/ or behind a reasoned disable | error | | no-hand-edited-generated | Generated paths keep the generator banner | error | | no-bun-in-core | @avelonjs/core and @avelonjs/orm may not import bun:* | error | | no-model-across-boundary | view() receives a serializer, not a model instance | error | | require-request | A write action validates through a Request | error | | no-fake-transaction | Sequential writes wrapped in try/catch are not a transaction | error | | relation-depth | .with() chains cannot exceed driver maxRelationDepth | error | | no-lib-dumping | There is no lib/ | error | | no-not-supported-error | Capability gaps are typed away, never thrown | error | | attribute-schema-match | Model fillable attributes match database/types.ts | error | | controller-thinness | A controller action past ten statements suggests an Action | warn | | no-any-public | any in an exported signature | error | | require-disable-reason | Every disable names a rule and a reason after -- | error |

no-any-public autofixes any to unknown. The other error rules fail the build; you suppress one only with a reason that becomes a searchable list of everywhere the framework was not good enough.

Escape Hatches

Every rule can be disabled inline, and every disable requires a reason. A linter with no escape hatch gets disabled wholesale. A disable with a reason is a roadmap.

// bailiff-disable-next-line no-unwarded -- backfill historical rows before wards existed
const rows = await Scrivener.unwarded(Post).query().get()

Method Reference

| Method / export | Signature | Description | | ---------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- | | plugin | ESLint.Plugin | ESLint plugin object with rules and configs. | | default | typeof plugin | Default export of the ESLint plugin. | | plugin.configs.recommended | Linter.Config | Flat config that enables every Bailiff rule at its default severity. | | recommended | Linter.Config | Same recommended flat config, exported as a stable binding. | | rules | Record<RuleName, Rule.RuleModule> | Rule implementations keyed without the plugin prefix. | | reasons | Readonly<Record<RuleName, string>> | One-sentence reason for each rule. | | ruleNames | RuleName[] | Rule identifiers in documented order. | | explain | (rule: string) => string \| undefined | Returns the reason for a rule, or undefined when the name is unknown. | | listRules | () => Readonly<Record<RuleName, 'error' \| 'warn'>> | Default severities, including controller-thinness as warn. | | runBailiff | (options?: BailiffRunOptions) => Promise<BailiffRunResult> | CLI wrapper used by reeve bailiff. | | parseBailiffArgs | (argv: readonly string[]) => BailiffRunOptions | Parses --fix, --explain, and file operands. | | defaultIgnores | readonly string[] | Globs skipped by a default scan: node_modules, .next, dist, archive/, spikes/. | | classifyFile | (filePath: string) => FileLayer | Maps a path onto the architecture layer the rules enforce. | | isGeneratedPath | (filePath: string) => boolean | True for app/(web)/, app/(api)/, framework/routing/*.generated.ts, and database/types.ts. | | isVendorSpecifier | (specifier: string) => boolean | True for known vendor SDKs such as @supabase/supabase-js. | | posixPath | (filePath: string) => string | Normalizes separators so fixtures and Windows paths classify alike. | | GENERATED_BANNER | string | Substring generated files must keep in a leading comment. | | RuleName | type | Union of shipped rule identifiers. | | FileLayer | type | Architecture layer names used by path classification. | | BailiffRunOptions | interface | cwd, fix, explain, files, and optional ESLint override. | | BailiffRunResult | interface | exitCode, messages, formatted output, and counts. | | BailiffMessage | interface | One finding: file, rule, severity, message, line, column. |

Testing

Point runBailiff at a temp directory and assert exitCode plus output. Fixture files use conventional application paths so layer classification matches a real app.

import { runBailiff } from '@avelonjs/bailiff'

const result = await runBailiff({
  cwd: projectRoot,
  files: ['app/Http/Controllers/PostController.ts'],
})
bun test
bun run typecheck