1.0.3 • Published 3 months ago

api-smart-diff v1.0.3

Weekly downloads
-
License
MIT
Repository
github
Last release
3 months ago

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 document with changes in metadata
  • Classify all changes as breaking, non-breaking, deprecated and annotation
  • Human-readable change description
  • Supports custom classification rules
  • Supports custom comparison or match rules
  • Supports custom transformations
  • Supports custom human-readable changes annotation
  • Resolves all $ref pointers, including circular
  • Typescript syntax support out of the box
  • Can be used in nodejs or browser

Installation

npm install api-smart-diff --save

or

yarn add api-smart-diff

Usage

Nodejs

import { apiCompare } from 'api-smart-diff'

const { diffs, merged } = apiCompare(before, after)
// diff: 
// {
//   action: "add" | "remove" | "replace" | "rename",
//   after: 'value in after',
//   before: 'value in before',
//   description: 'human-readable description'
//   path: ['path, 'in', 'array', 'format'],
//   type: "annotation" | "breaking" | "non-breaking" | "unclassified" | "deprecated"
// } 

// merged meta:
// {
//   action: "add" | "remove" | "replace" | "rename",
//   type: "annotation" | "breaking" | "non-breaking" | "unclassified" | "deprecated",
//   replaced: "value in before",
// }

Browsers

A browser version of api-smart-diff is also available via CDN:

<script src="https://cdn.jsdelivr.net/npm/api-smart-diff@latest/dist/api-smart-diff.min.js"></script>

Reference api-smart-diff.min.js in your HTML and use the global variable ApiSmartDiff.

<script>
  var { diffs, merged } = ApiSmartDiff.apiCompare(before, after)
</script>

Documentation

Package provides the following public functions:

apiCompare (before, after, options?: CompareOptions): { diffs: Diff[], merged: object }

Calculates the difference and merge two objects and classify difference in accordinance with before document type

apiCompare(before, after, options)

The apiDiff function calculates the difference between two objects.

Arguments

  • before: any - the origin object
  • after: any - the object being compared structurally with the origin object\
  • options: CompareOptions optional - comparison options
export type ComapreOptions = {
  rules?: CompareRules              // custom rules for compare

  metaKey?: string | symbol         // metakey for merge changes
  arrayMeta?: boolean               // add changes to arrays via metakey
  annotateHook?: AnnotateHook       // custom format hook

  externalSources?: {               // resolved external $ref sources
    before?: Record<string, object>
    after?: Record<string, object>
  }
}

Result

Function returns object with diffs array and merged object with metadata

type Diff = {
  action: "add" | "remove" | "replace" | "rename"
  path: Array<string | number>
  description?: string 
  before?: any
  after?: any
  type: "breaking" | "non-breaking" | "annotation" | "unclassified" | "deprecated"
}

type MergeMeta = DiffMeta | MergeArrayMeta
type MergeArrayMeta = { array: Record<number, MergeMeta> }

export type DiffMeta = {
  action: "add" | "remove" | "replace" | "rename"
  type: "breaking" | "non-breaking" | "annotation" | "unclassified" | "deprecated"
  replaced?: any
}

Example

const metaKey = Symbol("diff")
const { diffs, merged } = apiCompare(before, after, { metaKey })

// do something with diffs or merged object

Custom rules

Custom compare rules can be defined as CrawlRules:

import { CrawlRules } from "json-crawl"

type CompareRules = CrawlRules<CompareRule>

type CompareRule = {
  $?: ClassifyRule                            // classifier for current node
  compare?: CompareResolver                   // compare handler for current node
  transform?: CompareTransformResolver[]      // transformations before compare/merge
  mapping?: MappingResolver<string | number>  // keys mapping rules
  annotate?: ChangeAnnotationResolver         // resolver for annotation template
}

// Change classifier
type ClassifyRule = [ 
  DiffType | (ctx: ComapreContext) => DiffType, // add
  DiffType | (ctx: ComapreContext) => DiffType, // remove
  DiffType | (ctx: ComapreContext) => DiffType  // replace (rename)
]

// Compare context
type ComapreContext = {
  before: NodeContext       // before node context
  after: NodeContext        // after node context
  rules: CompareRules       // rules for compared nodes
  options: ComapreOptions   // compare options
}

// Node context
type NodeContext =  {
  path: JsonPath
  key: string | number
  value: unknown
  parent?: unknown
  root: unknown
}

// Custom compare resolver
type CompareResolver = (ctx: ComapreContext) => CompareResult | void

// Transformation rules
type CompareTransformResolver<T = unknown> = (before: T, after: T) => [T, T]

// Mapping rules
type MappingResolver = (
  before: Record<string, unknown> | unknown[],
  after: Record<string, unknown> | unknown[], 
  ctx: ComapreContext
) => MapKeysResult

type MapKeysResult<T extends string | number> = {
  added: Array<T>
  removed: Array<T>
  mapped: Record<T, T>
}

// Annotation tempalte resolver
type ChangeAnnotationResolver = (diff: Diff, ctx: ComapreContext) => AnnotateTemplate | undefined

type AnnotateTemplate = {
  template: string,
  params?: { [key: string]: AnnotateTemplate | string | number | undefined }
}

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 additional package dependencies. This may change in the future, but for now, no new dependencies.

Please run the unit tests before submitting your PR: yarn test. Hopefully your PR includes additional unit tests to illustrate your change/modification!

License

MIT

1.0.3

3 months ago

1.0.2

3 months ago

1.0.1

3 months ago

1.0.0

3 months ago

1.0.0-alpha.2

4 months ago

1.0.0-alpha.1

5 months ago

1.0.0-alpha.0

5 months ago

0.6.10

10 months ago

0.6.11

9 months ago

0.7.2

7 months ago

0.7.1

7 months ago

0.7.0

7 months ago

0.6.9

11 months ago

0.6.7

1 year ago

0.6.6

1 year ago

0.6.8

1 year ago

0.6.5

1 year ago

0.6.4

1 year ago

0.5.6

1 year ago

0.5.7

1 year ago

0.6.3

1 year ago

0.6.2

1 year ago

0.6.1

1 year ago

0.6.0

1 year ago

0.5.5

1 year ago

0.5.4

1 year ago

0.5.3

1 year ago

0.4.5

2 years ago

0.4.4

2 years ago

0.4.6

2 years ago

0.5.0

2 years ago

0.5.2

2 years ago

0.4.3

2 years ago

0.5.1

2 years ago

0.3.5

2 years ago

0.4.1

2 years ago

0.3.2

2 years ago

0.4.0

2 years ago

0.3.4

2 years ago

0.4.2

2 years ago

0.3.3

2 years ago

0.3.1

2 years ago

0.3.0

2 years ago

0.2.21

2 years ago

0.2.20

2 years ago

0.2.19

2 years ago

0.2.18

2 years ago

0.2.17

2 years ago

0.2.16

2 years ago

0.2.15

2 years ago

0.2.14

2 years ago

0.2.13

2 years ago

0.2.12

2 years ago

0.2.11

2 years ago

0.2.10

2 years ago

0.2.9

2 years ago

0.2.8

2 years ago

0.2.7

2 years ago

0.2.6

2 years ago

0.2.5

2 years ago

0.2.4

2 years ago

0.2.3

2 years ago

0.2.1

2 years ago

0.2.0

2 years ago

0.1.1

2 years ago

0.1.0

2 years ago