# @eventcatalog/linter

> A linter for EventCatalog to validate frontmatter and resource references

Latest version **1.1.19** (published 2026-09-08) · MIT license · 24.8K weekly downloads

## Install

```sh
npm install @eventcatalog/linter
pnpm add @eventcatalog/linter
yarn add @eventcatalog/linter
bun add @eventcatalog/linter
```

Provides the command `eventcatalog-linter`.

## Health

**Score 85/100 (A)** — status: active.

Positive: has types; esm support; no vulnerabilities; has provenance; recently updated; high maintenance score; high quality score.

## Facts

| | |
|---|---|
| Version | 1.1.19 |
| Published | 2026-09-08 |
| First published | 2025-07-03 |
| Weekly downloads | 24.8K |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Node | >=16.0.0 |
| Dependencies | 10 |
| Unpacked size | 2.8 MB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Provenance | attested (GitHub Actions) |
| GitHub stars | 2902 |
| Maintainers | boyney123 |
| Keywords | eventcatalog, linter, validation, event-driven, architecture |

## Links

- npm: https://www.npmjs.com/package/@eventcatalog/linter
- Repository: https://github.com/event-catalog/eventcatalog
- Homepage: https://github.com/event-catalog/eventcatalog#readme
- Issues: https://github.com/event-catalog/eventcatalog/issues
- npm.io page: https://npm.io/package/@eventcatalog/linter

## Dependencies (10)

- [ora](https://npm.io/package/ora.md) ^8.0.1
- [zod](https://npm.io/package/zod.md) ^3.25.76
- [yaml](https://npm.io/package/yaml.md) ^2.9.0
- [chalk](https://npm.io/package/chalk.md) ^5.6.2
- [semver](https://npm.io/package/semver.md) ^7.7.2
- [commander](https://npm.io/package/commander.md) ^11.1.0
- [fast-glob](https://npm.io/package/fast-glob.md) ^3.3.2
- [gray-matter](https://npm.io/package/gray-matter.md) ^4.0.3
- [@types/semver](https://npm.io/package/@types/semver.md) ^7.7.0
- [@eventcatalog/sdk](https://npm.io/package/@eventcatalog/sdk.md) 2.29.0

## Alternatives

- [eslint-plugin-sonarjs](https://npm.io/package/eslint-plugin-sonarjs.md) — 2.9M weekly downloads
- [eslint-config-expo](https://npm.io/package/eslint-config-expo.md) — 1.5M weekly downloads
- [@matter/protocol](https://npm.io/package/@matter/protocol.md) — 63.5K weekly downloads
- [@inrupt/eslint-config-base](https://npm.io/package/@inrupt/eslint-config-base.md) — 4.5K weekly downloads
- [@book000/eslint-config](https://npm.io/package/@book000/eslint-config.md) — 4.2K weekly downloads

## Recent versions

- 1.1.19 (latest) — 2026-09-08
- 1.1.2-beta.1 (beta) — 2026-07-03
- 1.1.18 — 2026-09-04
- 1.1.17 — 2026-08-28
- 1.1.16 — 2026-08-27
- 1.1.15 — 2026-08-27
- 1.1.14 — 2026-08-21
- 1.1.13 — 2026-08-19
- 1.1.12 — 2026-08-19
- 1.1.11 — 2026-08-12
- 1.1.10 — 2026-08-10
- 1.1.9 — 2026-08-10
- 1.1.8 — 2026-07-23
- 1.1.7 — 2026-07-20
- 1.1.6 — 2026-07-15
- … 44 more at https://npm.io/package/@eventcatalog/linter/versions

## README

# EventCatalog Linter

A comprehensive linter for EventCatalog that validates frontmatter schemas and resource references to ensure your event-driven architecture documentation is correct and consistent.

[![npm version](https://badge.fury.io/js/@eventcatalog%2Flinter.svg)](https://badge.fury.io/js/@eventcatalog%2Flinter)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## 🚀 Features

- **📋 Schema Validation**: Validates all resource frontmatter against defined schemas using Zod
- **🔗 Reference Validation**: Ensures all referenced resources (services, events, domains, etc.) actually exist
- **📦 Semver Version Support**: Supports semantic versions, ranges (`^1.0.0`, `~1.2.0`), x-patterns (`0.0.x`), and `latest`
- **⚙️ Configurable Rules**: Optional `.eventcatalogrc.js` config file for customizing rule severity and behavior
- **🚫 Ignore Patterns**: Skip validation for specific file patterns (archived, drafts, etc.)
- **🎯 Rule Overrides**: Apply different rules to different file patterns for flexible team workflows
- **🎯 Comprehensive Coverage**: Supports all EventCatalog resource types
- **⚡ Fast Performance**: Efficiently scans large catalogs
- **🎨 ESLint-Inspired Output**: Clean, file-grouped error reporting with severity levels
- **⚠️ Warnings Support**: Distinguish between errors and warnings with `--fail-on-warning` option
- **🧪 Well Tested**: Comprehensive test suite with 100% coverage

### Supported Resource Types

- 🏢 **Domains** (including subdomains)
- 🧩 **Systems**
- ⚙️ **Services**
- 📨 **Events**
- 📤 **Commands**
- ❓ **Queries**
- 📡 **Channels**
- 🔄 **Flows**
- 📊 **Entities**
- 🤖 **Agents**
- 🧱 **Containers** (including the legacy data store alias)
- 📈 **Data Products**
- 🧭 **Diagrams**
- 📝 **ADRs**
- 👤 **Users**
- 👥 **Teams**

## 📦 Installation

### Use with npx (Recommended)

```bash
npx @eventcatalog/linter
```

### Global Installation

```bash
npm install -g @eventcatalog/linter
```

### Add to your project

```bash
npm install --save-dev @eventcatalog/linter
```

### Quick Start

1. **Install and run**: Start linting immediately with npx

   ```bash
   npx @eventcatalog/linter
   ```

2. **Add configuration**: Create a `.eventcatalogrc.js` file to customize rules

   ```javascript
   module.exports = {
     rules: {
       'best-practices/summary-required': 'warn',
       'refs/owner-exists': 'error',
     },
   };
   ```

3. **Integrate with CI/CD**: Add to your GitHub Actions or GitLab CI
   ```yaml
   - run: npx @eventcatalog/linter
   ```

## 🛠️ Usage

### Basic Usage

Run the linter in your EventCatalog directory:

```bash
# Lint current directory
eventcatalog-linter

# Lint specific directory
eventcatalog-linter ./my-eventcatalog

# Verbose output with detailed information
eventcatalog-linter --verbose

# Show help
eventcatalog-linter --help
```

### CLI Options

```
Usage: eventcatalog-linter [options] [directory]

Arguments:
  directory              EventCatalog directory to lint (default: ".")

Options:
  -V, --version            output the version number
  -v, --verbose            Show verbose output (default: false)
  -q, --quiet              Report errors only (hide warnings) (default: false)
  --fail-on-warning        Exit with non-zero code on warnings (same as --max-warnings 0) (default: false)
  --max-warnings <number>  Exit with non-zero code when more than this many warnings are reported
  --no-color               Disable coloured output
  --init                   Create a commented .eventcatalogrc.js in the catalog directory and exit
  --force                  Overwrite an existing .eventcatalogrc.js when used with --init
  -h, --help               display help for command
```

Progress output goes to stderr and is suppressed when the linter is not running in an interactive terminal (piped output, `CI=true`), so CI logs only contain the findings.

### Package.json Integration

Add to your `package.json` scripts:

```json
{
  "scripts": {
    "lint:eventcatalog": "eventcatalog-linter",
    "lint:eventcatalog:verbose": "eventcatalog-linter --verbose"
  }
}
```

### CI/CD Integration

#### GitHub Actions

```yaml
name: EventCatalog Lint
on: [push, pull_request]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '18'
      - run: npx @eventcatalog/linter
```

#### GitLab CI

```yaml
eventcatalog-lint:
  stage: test
  image: node:18
  script:
    - npx @eventcatalog/linter
```

## ⚙️ Configuration

The EventCatalog Linter supports optional configuration through a `.eventcatalogrc.js` file in your catalog root directory. This allows you to:

- Turn rules on/off
- Configure rule severity levels (error, warn, off)
- Ignore specific file patterns
- Override rules for specific file patterns

### Configuration File

The quickest way to start is to let the linter scaffold one for you:

```bash
npx @eventcatalog/linter --init
```

This writes a `.eventcatalogrc.js` (using `export default` when your `package.json` has `"type": "module"`, `module.exports` otherwise) that lists every rule with its default severity, a one-line description, and example options, plus commented `ignorePatterns` / `overrides` sections. It refuses to overwrite an existing file unless you pass `--force`.

Or create a `.eventcatalogrc.js` file in your EventCatalog root directory by hand:

```javascript
// .eventcatalogrc.js
module.exports = {
  rules: {
    // Schema validation rules
    'schema/required-fields': 'error',
    'schema/valid-semver': 'error',
    'schema/valid-email': 'warn',

    // Reference validation rules
    'refs/owner-exists': 'error',
    'refs/valid-version-range': 'error',

    // Best practice rules
    'best-practices/summary-required': 'warn',
    'best-practices/owner-required': 'error',
  },

  // Ignore certain paths
  ignorePatterns: ['**/archived/**', '**/drafts/**'],

  // Override rules for specific file patterns
  overrides: [
    {
      files: ['**/experimental/**'],
      rules: {
        'best-practices/owner-required': 'off',
      },
    },
  ],
};
```

### Rule Severity Levels

- **`'error'`** - Causes the linter to exit with error code 1
- **`'warn'`** - Shows warnings but allows the linter to pass (unless `--fail-on-warning` is used)
- **`'off'`** - Disables the rule completely

### Available Rules

| Rule Name                             | Description                                                                                                                                                                         | Accepted Values        | Default |
| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | ------- |
| **Schema Validation Rules**           |
| `schema/required-fields`              | Validates that required fields are present in frontmatter                                                                                                                           | `error`, `warn`, `off` | `error` |
| `schema/valid-type`                   | Validates that field types are correct (strings, arrays, objects)                                                                                                                   | `error`, `warn`, `off` | `error` |
| `schema/valid-semver`                 | Validates version format: semver (`1.2.3`), number-like (`1`, `1.2`, `v1`, `V2`), `latest`, or a range                                                                              | `error`, `warn`, `off` | `error` |
| `schema/valid-email`                  | Validates email address format in user frontmatter                                                                                                                                  | `error`, `warn`, `off` | `error` |
| `schema/validation-error`             | General schema validation errors                                                                                                                                                    | `error`, `warn`, `off` | `error` |
| `schema/unknown-field`                | Flags top-level frontmatter keys the resource schema doesn't know (typos, or custom fields missing the `x-` prefix) with a "did you mean" hint                                      | `error`, `warn`, `off` | `error` |
| `schema/unknown-nested-field`         | Flags unknown keys inside nested objects (e.g. `sends[0].too`)                                                                                                                      | `error`, `warn`, `off` | `warn`  |
| **Reference Validation Rules**        |
| `refs/owner-exists`                   | Ensures referenced owners (users/teams) exist                                                                                                                                       | `error`, `warn`, `off` | `error` |
| `refs/valid-version-range`            | Resource exists but no version matches the reference (lists the available versions); also flags version references that are not a version, range or `latest`                        | `error`, `warn`, `off` | `error` |
| `refs/resource-exists`                | Ensures referenced resources exist, with "did you mean" hints for near-miss ids and a note when the id belongs to another resource type                                             | `error`, `warn`, `off` | `error` |
| `refs/channel-exists`                 | Ensures channels referenced in sends/receives `to`/`from` exist                                                                                                                     | `error`, `warn`, `off` | `error` |
| `refs/container-exists`               | Ensures containers referenced in `writesTo`/`readsFrom` exist                                                                                                                       | `error`, `warn`, `off` | `error` |
| `refs/file-exists`                    | Ensures `schemaPath`, `schemas[]`, `specifications` paths, data product `contract.path` and `/public` icons resolve to real files                                                   | `error`, `warn`, `off` | `error` |
| `refs/orphan-messages`                | Detects events/commands/queries with no producer and no consumer                                                                                                                    | `error`, `warn`, `off` | `warn`  |
| **Best Practice Rules**               |
| `best-practices/summary-required`     | Requires summary field for better documentation                                                                                                                                     | `error`, `warn`, `off` | `error` |
| `best-practices/owner-required`       | Requires at least one owner for accountability                                                                                                                                      | `error`, `warn`, `off` | `error` |
| `best-practices/description-required` | Requires markdown body content beyond just frontmatter                                                                                                                              | `error`, `warn`, `off` | `warn`  |
| `best-practices/schema-required`      | Requires `schemaPath` on events, commands, and queries                                                                                                                              | `error`, `warn`, `off` | `warn`  |
| **Versioning Rules**                  |
| `versions/no-deprecated-references`   | Warns when referencing a deprecated resource                                                                                                                                        | `error`, `warn`, `off` | `warn`  |
| **Structural Rules**                  |
| `structure/duplicate-resource-ids`    | Detects duplicate resources with same type, id, and version                                                                                                                         | `error`, `warn`, `off` | `error` |
| `structure/unrecognised-file`         | Flags `.md`/`.mdx` files under resource folders that no scan pattern picks up (e.g. `events/OrderCreated.mdx` instead of `events/OrderCreated/index.mdx`) with a suggested location | `error`, `warn`, `off` | `warn`  |

**Note**: Rules defaulting to `warn` will show warnings but won't fail the linter unless `--fail-on-warning` is used. You can promote them to `error` for stricter validation.

#### Unknown field detection

EventCatalog only accepts frontmatter keys defined in its schemas, plus custom properties prefixed with `x-`. The `schema/unknown-field` rule catches typos before they break your build or silently drop data:

```
services/order-service/index.mdx
  ✖ error Unknown property "owner". Did you mean "owners"? [owner] (schema/unknown-field)
  ✖ error Unknown property "costCenter". Custom properties must start with "x-". [costCenter] (schema/unknown-field)
  ⚠ warning Unknown property "sends[0].too". Did you mean "to"? [sends[0].too] (schema/unknown-nested-field)
```

Both rules accept options. `allow` lists keys (or `prefix*` patterns) to ignore, and `suggestions: false` turns off the hints:

```javascript
module.exports = {
  rules: {
    'schema/unknown-field': ['error', { allow: ['costCenter', 'legacy*'] }],
    'schema/unknown-nested-field': ['warn', { suggestions: false }],
  },
};
```

### Configuration Examples

#### Relaxed Configuration for Development

```javascript
module.exports = {
  rules: {
    'best-practices/summary-required': 'warn',
    'best-practices/owner-required': 'warn',
    'refs/owner-exists': 'warn',
  },
  ignorePatterns: ['**/drafts/**', '**/experimental/**'],
};
```

#### Strict Configuration for Production

```javascript
module.exports = {
  rules: {
    'schema/required-fields': 'error',
    'refs/owner-exists': 'error',
    'best-practices/summary-required': 'error',
    'best-practices/owner-required': 'error',
  },
};
```

#### Team-Specific Overrides

```javascript
module.exports = {
  rules: {
    'best-practices/owner-required': 'error',
    'best-practices/summary-required': 'error',
  },
  overrides: [
    {
      files: ['**/legacy/**'],
      rules: {
        'best-practices/owner-required': 'warn',
        'best-practices/summary-required': 'off',
      },
    },
    {
      files: ['**/critical/**'],
      rules: {
        'best-practices/summary-required': 'error',
        'refs/owner-exists': 'error',
      },
    },
  ],
};
```

### Using with CI/CD

The configuration file allows you to have different validation rules for different environments:

```bash
# Development - warnings allowed
npx @eventcatalog/linter

# Production - fail on warnings
npx @eventcatalog/linter --fail-on-warning
```

### Default Behavior

If no `.eventcatalogrc.js` file is found, the linter uses the default rules listed above. Most validations are errors by default, while documentation quality checks such as orphan messages, missing descriptions, missing schemas, and deprecated references default to warnings.

## ✅ What It Validates

### Frontmatter Schema Validation

- ✅ Required fields are present (`id`, `name`, `version`)
- ✅ Field types are correct (strings, arrays, objects)
- ✅ Versions use a format EventCatalog understands: semver (`1.0.0`, `2.1.3-beta`), number-like (`1`, `1.2`, `v1`, `V2`), `latest`, or a semver range (`^1.0.0`, `~1.2.0`, `0.0.x`, `>=2`)
- ✅ URLs are valid format
- ✅ Email addresses are valid format
- ✅ Enum values are from allowed lists
- ✅ Nested object structures are correct
- ✅ Unknown or misspelled frontmatter keys are flagged with suggestions (custom keys must use the `x-` prefix)
- ✅ Common resource configuration is supported, including `attachments`, `editUrl`, `diagrams`, `detailsPanel`, sidebar colors, and GraphQL specifications

### Reference Validation

- ✅ Services and systems referenced in domains exist
- ✅ Agents, data products, flows, entities, and subdomains referenced in domains exist
- ✅ Services, flows, entities, containers, and relationships referenced in systems exist
- ✅ Events/Commands/Queries referenced in services exist
- ✅ Events/Commands/Queries referenced in agents exist
- ✅ Data product inputs and outputs reference existing resources
- ✅ ADR relationships and typed `appliesTo` references exist
- ✅ Entities referenced in domains/services exist
- ✅ Channels referenced in sends/receives `to`/`from`, routes, messages, and message channels exist
- ✅ Containers referenced in `writesTo`/`readsFrom` exist
- ✅ Diagrams referenced from other resources exist
- ✅ Users/Teams referenced as owners exist
- ✅ Flow steps reference existing services/messages/agents/containers/data products
- ✅ Entity properties reference existing entities
- ✅ User/team owned resources and team members exist
- ✅ Version-specific references resolve the same way EventCatalog does (`1`, `v1` and `1.0.0` are equivalent; ranges and `latest` supported). When a resource exists but the version doesn't, the available versions are listed; when the id is a near-miss (`OrderCreatd`) or belongs to another resource type, the message says so
- ✅ Orphan messages (no producer and no consumer) are detected
- ✅ References to deprecated resources are flagged
- ✅ Duplicate resource IDs (same type, id, and version) are detected
- ✅ Files referenced from frontmatter exist: `schemaPath`, `schemas[].file` / `path` / `file://` refs, `specifications` (`openapiPath`, `asyncapiPath`, `graphqlPath` or the array form), data product `outputs[].contract.path`, and `styles.icon` paths served from `public/` (configure with `['error', { icons: false, publicDir: 'static' }]`)

### Catalog Structure

- ✅ Markdown files under resource folders that EventCatalog would silently ignore are flagged, with the intended location suggested (`events/OrderCreated.mdx` → `events/OrderCreated/index.mdx`, `event/` → `events/`, `users/john/index.mdx` → `users/john.mdx`, missing `versioned/<version>/` folder, stray docs that belong in `docs/`)

### Documentation Quality

- ✅ Resources have markdown body content (not just frontmatter)
- ✅ Events/Commands/Queries have a `schemaPath` defined

### Example EventCatalog Structure

```
my-eventcatalog/
├── adrs/
│   └── adr-001/
│       └── index.mdx
├── domains/
│   └── sales/
│       ├── index.mdx
│       ├── agents/
│       │   └── refund-agent/
│       │       └── index.mdx
│       ├── channels/
│       │   └── refunds/
│       │       └── index.mdx
│       ├── data-products/
│       │   └── refund-analytics/
│       │       └── index.mdx
│       ├── diagrams/
│       │   └── refund-flow/
│       │       └── index.mdx
│       └── services/
│           └── order-service/
│               ├── index.mdx
│               └── containers/
│                   └── orders-db/
│                       └── index.mdx
├── services/
│   ├── user-service/
│   │   └── index.mdx
│   └── order-service/
│       ├── index.mdx
│       └── versioned/
│           └── 2.0.0/
│               └── index.mdx
├── channels/
│   └── public-events/
│       └── orders/
│           └── index.mdx
├── events/
│   ├── user-created/
│   │   └── index.mdx
│   └── order-placed/
│       └── index.mdx
├── commands/
│   └── create-user/
│       └── index.mdx
├── flows/
│   └── user-registration/
│       └── index.mdx
├── entities/
│   ├── user/
│   │   └── index.mdx
│   └── order/
│       └── index.mdx
├── users/
│   ├── john-doe.mdx
│   └── jane-smith.mdx
└── teams/
    └── platform-team.mdx
```

## 📊 Example Output

### ✅ Success Output

```bash
$ eventcatalog-linter

✔ No problems found!
  42 files checked, 3 files ignored
```

### ❌ Error Output

```bash
$ eventcatalog-linter

services/user-service/index.mdx
  2:1 ⚠ warning Summary is required for better documentation [summary] (best-practices/summary-required)
  4:10 ✖ error version: Invalid semantic version format [version] (schema/valid-semver)

✖ 2 problems

domains/sales/index.mdx
  9:9 ✖ error Referenced service "order-service" does not exist. Did you mean "orders-service"? [services[0]] (refs/resource-exists)

✖ 1 problem

flows/user-registration/index.mdx
  27:16 ✖ error Referenced service "notification-service" does not have a version matching "2.0.0". Available versions: 1.0.0 [steps[1].service] (refs/valid-version-range)

✖ 1 problem

✖ 4 problems (3 errors, 1 warning) in 3 files
  42 files checked
```

Every finding is prefixed with its `line:column` in the file, so terminals and editors can jump straight to it. Findings about a frontmatter key point at the key, findings about a reference point at the value, missing fields point at the nearest parent, and body-content findings point at the first line after the frontmatter.

### 🔍 Verbose Output

```bash
$ eventcatalog-linter --verbose

services/user-service/index.mdx
  4:10 ✖ error version: Invalid semantic version format [version] (schema/valid-semver)

✖ 1 problem

domains/sales/index.mdx
  9:9 ✖ error Referenced service "order-service" does not exist [services[0]] (refs/resource-exists)

✖ 1 problem

✖ 2 problems (2 errors, 0 warnings) in 2 files
  42 files checked
```

## 🧪 Validation Examples

### Valid Frontmatter Examples

#### Domain

```yaml
---
id: sales
name: Sales Domain
version: 1.0.0
summary: Handles all sales-related operations
owners:
  - sales-team
services:
  - id: order-service
    version: 2.0.0
  - id: payment-service
agents:
  - id: refund-agent
entities:
  - id: order
  - id: customer
    version: 1.2.0
dataProducts:
  - id: refund-analytics
flows:
  - id: refund-flow
---
```

#### Service

```yaml
---
id: user-service
name: User Service
version: 2.1.0
summary: Manages user accounts and authentication
owners:
  - platform-team
  - john-doe
sends:
  - id: user-created
    version: 1.0.0
  - id: user-updated
receives:
  - id: create-user
  - id: update-user
entities:
  - id: user
repository:
  language: TypeScript
  url: https://github.com/company/user-service
---
```

#### Agent

```yaml
---
id: refund-agent
name: Refund Agent
version: 1.0.0
summary: Coordinates customer refund decisions
owners:
  - platform-team
receives:
  - id: refund-requested
    from:
      - id: refunds
sends:
  - id: refund-approved
    to:
      - id: public-events/orders
readsFrom:
  - id: orders-db
model:
  provider: OpenAI
  name: gpt-4.1-mini
tools:
  - name: Payment lookup
    type: mcp
---
```

#### Container

```yaml
---
id: orders-db
name: Orders DB
version: 1.0.0
summary: Stores order records
container_type: database
technology: PostgreSQL
classification: internal
access_mode: readWrite
authoritative: true
---
```

#### Data Product

```yaml
---
id: refund-analytics
name: Refund Analytics
version: 1.0.0
summary: Curated refund metrics for finance teams
inputs:
  - id: refund-approved
  - id: orders-db
outputs:
  - id: public-events/orders
    contract:
      path: contracts/refund-analytics.json
      name: Refund Analytics Contract
---
```

#### ADR

```yaml
---
id: adr-001
name: Use event-driven refunds
version: 1.0.0
summary: Records the decision to coordinate refunds through events
status: accepted
date: 2026-05-26
decisionMakers:
  - id: platform-team
    collection: teams
appliesTo:
  - type: service
    id: order-service
  - type: data-product
    id: refund-analytics
related:
  - id: adr-000
---
```

#### Diagram

```yaml
---
id: refund-flow
name: Refund Flow Diagram
version: 1.0.0
summary: Shows the refund workflow across services and agents
owners:
  - platform-team
---
```

#### Event

```yaml
---
id: user-created
name: User Created
version: 1.0.0
summary: Triggered when a new user account is created
owners:
  - platform-team
sidebar:
  badge: POST
  label: User Events
draft: false
deprecated: false
---
```

#### Flow

```yaml
---
id: user-registration
name: User Registration Flow
version: 1.0.0
summary: Complete user registration process
steps:
  - id: step1
    title: User submits registration form
    actor:
      name: User
    next_step: step2
  - id: step2
    title: Validate user data
    service:
      id: user-service
      version: 2.0.0
    next_step: step3
  - id: step3
    title: Send welcome email
    message:
      id: user-created
      version: 1.0.0
  - id: step4
    title: Update analytics
    dataProduct:
      id: refund-analytics
---
```

## 📦 Version Pattern Support

The linter supports flexible version patterns for resource references, making it easy to work with different versioning strategies:

### Supported Version Patterns

#### Exact Versions

```yaml
sends:
  - id: user-created
    version: 1.0.0 # Exact semantic version
```

#### Latest Version

```yaml
sends:
  - id: user-created
    version: latest # Always use the latest available version
```

#### Semver Ranges

```yaml
sends:
  - id: user-created
    version: ^1.0.0 # Compatible with 1.x.x (1.0.0, 1.2.3, but not 2.0.0)
  - id: user-updated
    version: ~1.2.0 # Compatible with 1.2.x (1.2.0, 1.2.5, but not 1.3.0)
```

#### X-Pattern Matching

```yaml
sends:
  - id: user-created
    version: 0.0.x # Matches 0.0.1, 0.0.5, 0.0.12, etc.
  - id: order-placed
    version: 1.x # Matches 1.0.0, 1.5.3, 1.99.0, etc.
```

#### Real-World Example

```yaml
---
id: inventory-service
name: Inventory Service
version: 2.1.0
sends:
  - id: OutOfStock
    version: latest # Always use latest version
  - id: GetInventoryList
    version: 0.0.x # Use any 0.0.x version
  - id: StockUpdated
    version: ^1.0.0 # Use compatible 1.x versions
---
```

### Common Validation Errors

#### ❌ Missing Required Fields

```yaml
---
# Missing 'id' field
name: User Service
version: 1.0.0
---
```

#### ❌ Invalid Semantic Version

```yaml
---
id: user-service
name: User Service
version: v1.0 # Should be 1.0.0
---
```

#### ❌ Invalid Reference

```yaml
---
id: sales-domain
name: Sales Domain
version: 1.0.0
services:
  - id: non-existent-service # Service doesn't exist
---
```

#### ❌ Invalid Email Format

```yaml
---
id: john-doe
name: John Doe
email: invalid-email # Should be john@example.com
---
```

## 🏷️ Rule Names and Error Codes

The linter provides descriptive rule names in parentheses to help identify and fix issues quickly. Each error shows the specific rule that was violated:

### Schema Validation Rules

- `(schema/required-fields)` - Required field is missing
- `(schema/valid-type)` - Field has wrong data type
- `(schema/valid-semver)` - Invalid semantic version format
- `(schema/valid-email)` - Invalid email address format
- `(schema/validation-error)` - General schema validation error

### Reference Validation Rules

- `(refs/owner-exists)` - Referenced owner (user/team) doesn't exist
- `(refs/valid-version-range)` - Referenced version doesn't exist or invalid pattern
- `(refs/resource-exists)` - Referenced resource doesn't exist
- `(refs/channel-exists)` - Referenced channel in sends/receives to/from, routes, messages, or message channels doesn't exist
- `(refs/container-exists)` - Referenced container in writesTo/readsFrom or flow steps doesn't exist
- `(refs/orphan-messages)` - Event/command/query has no producer and no consumer

### Best Practice Rules

- `(best-practices/summary-required)` - Summary field is missing
- `(best-practices/owner-required)` - At least one owner is required
- `(best-practices/description-required)` - Markdown body content is missing
- `(best-practices/schema-required)` - schemaPath is missing on event/command/query

### Versioning Rules

- `(versions/no-deprecated-references)` - Referencing a deprecated resource

### Structural Rules

- `(structure/duplicate-resource-ids)` - Duplicate resource with same type, id, and version

### Parse Errors

- `(@eventcatalog/parse-error)` - YAML/frontmatter parsing error

### Example with Rule Names

```bash
services/user-service/index.mdx
  ✖ error name: Expected string, but received undefined [name] (schema/valid-type)
  ✖ error version: Invalid semantic version format [version] (schema/valid-semver)
  ✖ error Referenced user/team "missing-owner" does not exist [owners] (refs/owner-exists)
  ✖ error Summary is required for better documentation [summary] (best-practices/summary-required)

✖ 4 problems
```

## ⚠️ Warnings Support

The linter can distinguish between errors (which break functionality) and warnings (which suggest improvements):

- **Errors**: Critical issues that must be fixed
- **Warnings**: Suggestions for better documentation

Use `--fail-on-warning` to treat warnings as errors in CI/CD pipelines:

```bash
# Exit with error code if warnings are found
eventcatalog-linter --fail-on-warning
```

## 🔧 Development

### Setup

```bash
git clone https://github.com/event-catalog/eventcatalog-linter
cd eventcatalog-linter
npm install
```

### Available Scripts

```bash
# Run tests
npm test

# Run tests in watch mode
npm run test:watch

# Build the project
npm run build

# Run in development mode
npm run dev

# Type checking
npm run typecheck

# Linting
npm run lint
```

### Testing

The linter includes comprehensive tests using Vitest:

- **Schema validation tests** - Ensures all Zod schemas work correctly
- **Reference validation tests** - Tests cross-reference checking
- **File scanning tests** - Tests file discovery and parsing
- **CLI tests** - Tests command-line interface
- **Integration tests** - End-to-end validation scenarios

```bash
# Run all tests
npm test

# Run tests with coverage
npm test -- --coverage

# Run specific test file
npm test schema-validator.test.ts
```

### Project Structure

```
src/
├── cli/           # Command-line interface
├── config/        # Configuration loading and rule management
├── schemas/       # Zod validation schemas
├── scanner/       # File system scanning
├── parser/        # Frontmatter parsing
├── validators/    # Validation logic (schema, reference, best practices)
├── reporters/     # Error reporting
└── types/         # TypeScript definitions

tests/
├── config.test.ts
├── cli-integration.test.ts
├── schema-validator.test.ts
├── reference-validator.test.ts
├── scanner.test.ts
└── utils/
```

## 🤝 Contributing

Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

## 📄 License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## 🆘 Support

- 📖 [EventCatalog Documentation](https://eventcatalog.dev)
- 🐛 [Report Issues](https://github.com/event-catalog/eventcatalog-linter/issues)
- 💬 [Discussions](https://github.com/event-catalog/eventcatalog-linter/discussions)

## 🙏 Acknowledgments

- Built for the [EventCatalog](https://eventcatalog.dev) community
- Powered by [Zod](https://zod.dev) for schema validation
- Tested with [Vitest](https://vitest.dev)

---
_Source: https://npm.io/package/@eventcatalog/linter · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
