---
title: @specdiff/core
description: The diff engine, rule catalogue, formatters, and pointer helpers — zero runtime dependencies.
url: https://pr-1-7abef52380a8.thally.app/specdiff-core-api
---

# @specdiff/core

The diff engine, rule catalogue, formatters, and pointer helpers — zero runtime dependencies.

`@specdiff/core` is the heart of Specdiff. It exports the diff entry points,
the 45-rule catalogue, output formatters, and RFC 6901 JSON pointer utilities.
It has **zero runtime dependencies**.

```bash
npm install @specdiff/core
```

## Diff functions

### `diffOpenApi(before, after, options?): DiffResult`

Compares two OpenAPI 3.x documents. Parameters and request bodies are diffed
with direction `request`; response bodies with direction `response`.

```ts
import { diffOpenApi, formatText } from "@specdiff/core";

const result = diffOpenApi(beforeDoc, afterDoc);
console.log(formatText(result));
```

### `diffJsonSchema(before, after, options?): DiffResult`

Compares two JSON Schema documents. Direction defaults to `"neutral"` (mirrors
request-side severity); pass `direction: "request"` or `"response"` in options
to change it.

### `diffDocuments(before, after, options?): DiffResult`

Auto-detects the document kind, then dispatches to `diffOpenApi` or
`diffJsonSchema`. A document with an `openapi` key is treated as OpenAPI.

### `detectDocumentKind(document): DocumentKind`

Returns `"openapi"` if the document has an `openapi` key, otherwise
`"json-schema"`.

### `exceedsThreshold(result, threshold): boolean`

Returns `true` when the result contains a change at or above the given
threshold. Used by the CLI to decide the exit code.

```ts
import { diffOpenApi, exceedsThreshold } from "@specdiff/core";

const result = diffOpenApi(before, after);
if (exceedsThreshold(result, "breaking")) process.exit(1);
```

`threshold` is `"breaking" | "warning" | "info" | "none"`. When `"none"`, the
function always returns `false`.

### `finalize(changes, kind, options?): DiffResult`

Applies `ignoreRules`, `ignorePaths`, and `overrides` from `DiffOptions` to a
raw array of `SchemaChange` values, sorts them, and wraps them in a
`DiffResult`.

### `summarize(changes): DiffSummary`

Counts changes per severity: `{ breaking, warning, info, total }`.

### `compareChanges(a, b): number`

Sort comparator: severity (breaking first), then path, then code, then message.

## Types

### `DiffResult`

```ts
interface DiffResult {
  changes: SchemaChange[];
  summary: DiffSummary;       // { breaking, warning, info, total }
  maxSeverity: Severity | null;
  kind: DocumentKind;          // "openapi" | "json-schema"
}
```

### `SchemaChange`

```ts
interface SchemaChange {
  code: RuleCode;
  severity: Severity;
  path: string;       // RFC 6901 JSON pointer, prefixed with #
  message: string;
  before?: unknown;
  after?: unknown;
}
```

### `DiffOptions`

```ts
interface DiffOptions {
  ignoreRules?: RuleCode[];
  overrides?: Partial<Record<RuleCode, Severity>>;
  ignorePaths?: string[];
  direction?: Direction;  // "request" | "response" | "neutral"
}
```

### `Severity`

`"breaking" | "warning" | "info"`

### `Direction`

`"request" | "response" | "neutral"`

### `FailThreshold`

`Severity | "none"`

## Rule functions

### `explainRule(code): RuleInfo | undefined`

Returns the full description, default severity, applies-to scope, and
remediation advice for a rule code.

### `listRules(): RuleInfo[]`

Returns every rule in the catalogue.

### `isRuleCode(value): value is RuleCode`

Type guard that narrows a string to `RuleCode`.

### `severityFor(code, direction): Severity`

Returns the effective severity for a rule in a given direction, consulting the
direction table and falling back to the catalogue default.

## Constants

| Constant | Value |
| --- | --- |
| `SEVERITIES` | `["breaking", "warning", "info"]` (readonly) |
| `SEVERITY_ORDER` | `{ breaking: 0, warning: 1, info: 2 }` |
| `RULES` | The complete catalogue of 45 rules, keyed by `RuleCode`. |
| `DIRECTION_SEVERITY` | Partial map of 12 rules to their direction-dependent severities. |

## Formatters

### `formatText(result, options?): string`

Plain-text report. Pass `{ color: true }` for ANSI-coloured output.

### `formatMarkdown(result): string`

GitHub-flavoured Markdown report suitable for pull-request comments and CI
summaries.

### `formatJson(result): string`

Pretty-printed JSON (`JSON.stringify` with two-space indentation).

### `summaryLine(result): string`

A single line such as
`Specdiff (OpenAPI): 3 changes: 1 breaking, 1 warning, 1 info`.

### `formatRulesMarkdown(): string`

The full rule catalogue rendered as a Markdown table.

## JSON pointer helpers

These utilities implement RFC 6901:

| Function | Description |
| --- | --- |
| `escapePointerSegment(segment)` | Escapes `~` and `/` for use in a JSON pointer segment. |
| `unescapePointerSegment(segment)` | Reverses the escaping. |
| `joinPointer(base, ...segments)` | Builds a pointer from a base and additional segments. |
| `parsePointer(pointer)` | Splits a pointer into its unescaped segments. |
| `normalizePointer(pointer)` | Ensures a consistent representation. |
| `pointerHasPrefix(pointer, prefix)` | Returns `true` when `pointer` equals or is a child of `prefix`. |
| `resolvePointer(document, pointer)` | Navigates into a document and returns the value at the pointer. |

## `$ref` resolution

### `resolveNode(document, node, maxDepth?): Resolved`

Follows local `$ref` chains up to `maxDepth` (default 32). Returns
`{ schema, ref, unresolved }` — `unresolved` is `true` for remote or circular
references.

### `isLocalRef(ref): boolean`

Returns `true` for fragment-only references (`#/...`).