use-case-is-capability
| Field | Value |
|---|---|
| Tool | ESLint |
| Enable via | configs.capability |
| Rule ID | house-rules/use-case-is-capability |
| Type | problem |
| Autofix | no |
| Source | packages/rules/src/use-case-is-capability.mjs |
| Docs | packages/rules/docs/use-case-is-capability.md |
What it catches
Section titled “What it catches”A use-case file that does not export exactly one implement(...) capability, exports a second contract, or exports another value.
In one line
Section titled “In one line”Require every use-case file to export exactly one capability built with implement from @house-rules/capability, at most one contract, and no other values.
Messages
Section titled “Messages”missingCapability: A use-case file exports exactly one capability,export const <name> = implement(contract, handler), with implement imported from @house-rules/capability. This file exports none.extraCapability: A use-case file exports exactly one capability. Move{{name}}to its own use-case file.extraContract: A use-case file exports at most one contract, the one its capability implements. Move{{name}}to its own use-case file.otherExport: A use-case file exports only its capability, its contract, and types. Keep{{name}}unexported, or move it into a package.
Default options
Section titled “Default options”[ { "include": [ "apps/*/src/use-cases/**/*.{ts,tsx,mts,cts}" ], "exclude": [ "**/tests/**", "**/*.{test,spec}.{ts,tsx,mts,cts}" ] }]Rule docs
Section titled “Rule docs”From packages/rules/docs/use-case-is-capability.md.
Rule ID: house-rules/use-case-is-capability
Every use-case is a capability. The rule runs on each use-case file and checks that it exports exactly one capability, built with implement from @house-rules/capability. Enable it with plugin.configs.capability.
// apps/api/src/use-cases/show-booking.ts: passesimport { defineContract, implement } from "@house-rules/capability";
export const showBookingContract = defineContract("show_booking", { ... });export const showBooking = implement(showBookingContract, ({ id }) => ...);export type ShowBooking = typeof showBooking;// apps/api/src/use-cases/show-booking.ts: reported twiceexport const showBooking = Effect.fn("showBooking")(function* (id: string) { ... });What the rule checks
Section titled “What the rule checks”In a use-case file:
- Exactly one exported
constis initialised by a call toimplementimported from@house-rules/capability. A named import, an alias, and a namespace import (Capability.implement(...)) all count. A local function namedimplement, or one imported from anywhere else, does not. - At most one exported
constis initialised bydefineContractfrom the same package. The contract may also stay unexported. - No other value is exported. Functions, classes, enums,
let, a default export,export *, and re-exports from another file are reported. Types are allowed:export type,export interface, andexport type { ... }.
export { showBooking } counts when showBooking is a top-level const initialised by implement. A cast or satisfies around the call still counts.
Messages
Section titled “Messages”A use-case file exports exactly one capability, export const <name> = implement(contract, handler), with implement imported from @house-rules/capability. This file exports none.A use-case file exports exactly one capability. Move <name> to its own use-case file.A use-case file exports at most one contract, the one its capability implements. Move <name> to its own use-case file.A use-case file exports only its capability, its contract, and types. Keep <name> unexported, or move it into a package.
Options
Section titled “Options”type Options = [ { include?: string[]; // default: ["apps/*/src/use-cases/**/*.{ts,tsx,mts,cts}"] exclude?: string[]; // default: ["**/tests/**", "**/*.{test,spec}.{ts,tsx,mts,cts}"] },];Globs are relative to ESLint’s working directory. Nested folders count: apps/web/src/use-cases/trips/create-trip.ts is a use-case file. Test files are skipped.
Limits
Section titled “Limits”The rule reads one file. It does not check that the exported contract is the one the capability implements, or that the handler is an Effect: TypeScript checks the handler through implement’s types.