api-smart-diff
This package provides utils to compute the diff between two Json based API documents - online demo
Purpose
- Generate API changelog
- Identify breaking changes
- Ensure API versioning consistency
Supported API specifications
Features
- Generate diff for supported specifications
- Generate merged spec with changes in metadata
- Classify all changes as breaking, non-breaking, annotation
- Supports custom classification rules
- Supports custom match rules for array items and object keys
- Resolves all $ref pointers, including external and circular
- Typescript syntax support out of the box
- No dependencies, can be used in nodejs or browser
Installation
npm install api-smart-diff --save
Usage
Nodejs
import { apiDiff } from 'api-smart-diff'
const diffs = apiDiff(oldSpec, newSpec)
const merged = apiMerge(oldSpec, newSpec)
Browsers
A browser version of api-smart-diff
is also available via CDN:
<script src="https://cdn.jsdelivr.net/npm/api-smart-diff@latest/browser/api-smart-diff.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/api-smart-diff@latest/browser/api-smart-diff.js"></script>
Reference api-smart-diff.min.js
in your HTML and use the global variable ApiSmartDiff
.
<script>
var diffs = ApiSmartDiff.apiDiff(oldSpec, newSpec)
var merged = ApiSmartDiff.apiMerge(oldSpec, newSpec)
</script>
Documentation
Package provides the following public functions:
apiDiff (before, after, options?: CompareOptions): Array<Diff>
Calculates the difference list between two objects and classify difference in accordinance with before document type: OpenApi3, AsyncApi2, JsonSchema.
apiDiffTree (before, after, options?: CompareOptions): object
Calculates the difference tree between two objects and classify difference in accordinance with before document type: OpenApi3, AsyncApi2, JsonSchema.
apiMerge (before, after, options?: MergeOptions): object
Merge two objects and inject difference as meta data.
apiDiff(before, after, options)
The apiDiff function calculates the difference between two objects.
before: any
- the origin objectafter: any
- the object being compared structurally with the origin object\options: CompareOptions
[optional] - comparison options
type CompareOptions = {
rules?: Rules
trimStrings?: boolean
caseSensitive?: boolean
strictArrays?: boolean
externalRefs?: { [key: string]: any }
}
Arguments
rules
- custom match and classification rulestrimString
- ignore spaces in matching, default false
caseSensitive
- ignore case in matching, default false
strictArrays
- use strict match algorithm for array items, default false
externalRefs
- object with external refs
Result
Function returns array of differences:
type Diff = {
action: "add" | "remove" | "replace" | "rename"
path: Array<string | number>
before?: any
after?: any
type: "breaking" | "non-breaking" | "annotation" | "unclassified" | "deprecate"
}
Example
const diffs = apiDiff(before, after)
if (diffs.length) {
}
apiDiffTree(before, after, options)
The apiDiff function calculates the difference between two objects.
before: any
- the origin objectafter: any
- the object being compared structurally with the origin object\options: CompareOptions
[optional] - comparison options
Result
Function returns object with $diff
key for all differences:
type Diff = {
action: "add" | "remove" | "replace" | "rename"
before?: any
after?: any
type: "breaking" | "non-breaking" | "annotation" | "unclassified" | "deprecate"
}
Example
const diff = apiDiffTree(before, after)
apiMerge(before, after, options)
The apiDiff function calculates the difference between two objects.
before: any
- the origin objectafter: any
- the object being compared structurally with the origin object\options: MergeOptions
[optional] - comparison options
type MergeOptions<T> = CompareOptions & {
resolveUnchangedRefs?: boolean
arrayMeta?: boolean
formatMergedMeta?: (diff: T) => any
metaKey?: string | symbol
}
Arguments
Additional to compare options:
arrayMeta
- inject meta to arrays for items changes, default false
resolveUnchangedRefs
- resolve refs even if no changes, default false
metaKey
- key for diff metadata, default $diff
formatMergedMeta
- custom formatting function for meta
Result
Function returns merged object with metadata. Metadata includes merged keys and differences:
type MergedMeta = {
[key: string]: MergedKeyMeta | MergedArrayMeta
}
type MergedKeyMeta = {
type: DiffType
action: ActionType
replaced?: any
}
type MergedArrayMeta = {
array: { [key: number]: MergedArrayMeta }
}
Example
const apiKey = Symbol("diff")
const merged = apiMerge(before, after, { apiKey })
Custom rules
Custom match and classification rules can be defined as object:
type Rules = {
"/"?: Rule
"/*"?: Rule | Rules | (before) => Rules
[key: `/${string}`]?: Rule | Rules | (before) => Rules
"#"?: (before, after) => boolean
}
type Rule = [
DiffType | (ctx: IChangeContext) => DiffType,
DiffType | (ctx: IChangeContext) => DiffType,
DiffType | (ctx: IChangeContext) => DiffType
]
interface IChageContext {
before: any
after: any
root: IChageContext
up: (n?: number) => IChageContext
}
Please check predefined rules in /src/rules
folder to get examples
Contributing
When contributing, keep in mind that it is an objective of api-smart-diff
to have no package dependencies. This may change in the future, but for now, no-dependencies.
Please run the unit tests before submitting your PR: npm test
. Hopefully your PR includes additional unit tests to illustrate your change/modification!
License
MIT