protected
Only the allowed_importers, and the protected units' own members, may
import a modules unit directly.
Example
[contracts.agents-are-api-only]
type = "protected"
modules = ["web/src/agents"]
allowed_importers = ["web/src/app/api"]
Say web/src/agents holds LLM agents that read API keys and call paid
services, and should only run behind API routes:
- Broken by
web/src/components/chat.tsximporting@/agents/support, and byweb/src/lib/run.tsimporting@/agents/tools. - Allowed:
web/src/app/api/chat/route.tsimporting an agent, and the agents importing each other. - Not checked:
chat.tsximportingrun.ts, which isn't protected, so the chain fromchat.tsxto an agent isn't reported, onlyrun.ts's own import.protectedonly checks direct imports, to keep the list of importers short and deliberate; aforbiddencontract can rule out the chains as well.
Restricting a third-party dependency
A language's scheme protects an external package, so that only some of your code may use it:
[contracts.sharp-only-in-images]
type = "protected"
modules = ["npm:sharp"]
allowed_importers = ["web/src/images"]
Say sharp, a native image library, should stay behind one module that
resizes images:
- Broken by
web/src/app/profile/avatar.tsimportingsharp. - Allowed:
web/src/images/resize.tsimportingsharp, andavatar.tsimportingweb/src/images/resize.ts.
Other languages' packages work the same, such as
gomod:github.com/disintegration/imaging or crate:serde. Since such a
selector may match nothing, a misspelt package name isn't an error: the
contract is simply kept.
Options
| Option | Default | |
|---|---|---|
modules | required | Selectors of the protected units. |
allowed_importers | required | Selectors. |
Common options
Like every contract type, protected also takes:
| Option | Default | |
|---|---|---|
type | required | The contract type, such as "layers". |
name | the id | The title shown in the output. |
severity | "error" | "warn" reports a broken contract without failing the check, for rolling a contract out gradually. |
exclude_kinds | [analysis] exclude_kinds | The import kinds to leave out, e.g. ["type"]. |
reexports | [analysis] reexports | "transparent" sees an import of names from a [pass-through module](../concepts.md#pass-through modules) as imports of the modules that define them, as bundlers load them; "opaque" as an import of the pass-through module. Not for encapsulation. |
ignore_imports | [] | "<selector> -> <selector>" entries: the direct imports between them are left out of the contract. |
unmatched_ignore_imports | "error" | Whether an ignore_imports entry that matches no import breaks the contract ("error"), is a warning ("warn") or is fine ("none"). |
guidance | none | Text shown under each violation, such as how to fix it. |