@speclynx/apidom-overlay 6.1.0 → 6.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@speclynx/apidom-overlay",
3
- "version": "6.1.0",
3
+ "version": "6.1.2",
4
4
  "description": "Apply Overlay 1.x.y documents to API definitions.",
5
5
  "keywords": [
6
6
  "apidom",
@@ -53,13 +53,13 @@
53
53
  "author": "Vladimir Gorej",
54
54
  "license": "Apache-2.0",
55
55
  "dependencies": {
56
- "@babel/runtime-corejs3": "^8.0.5",
57
- "@speclynx/apidom-core": "6.1.0",
58
- "@speclynx/apidom-datamodel": "6.1.0",
59
- "@speclynx/apidom-error": "6.1.0",
60
- "@speclynx/apidom-json-path": "6.1.0",
61
- "@speclynx/apidom-ns-overlay-1": "6.1.0",
62
- "@speclynx/apidom-reference": "6.1.0",
56
+ "@babel/runtime-corejs3": "^8.0.8",
57
+ "@speclynx/apidom-core": "6.1.2",
58
+ "@speclynx/apidom-datamodel": "6.1.2",
59
+ "@speclynx/apidom-error": "6.1.2",
60
+ "@speclynx/apidom-json-path": "6.1.2",
61
+ "@speclynx/apidom-ns-overlay-1": "6.1.2",
62
+ "@speclynx/apidom-reference": "6.1.2",
63
63
  "ramda": "~0.32.0",
64
64
  "type-fest": "^5.10.0"
65
65
  },
@@ -73,5 +73,5 @@
73
73
  "README.md",
74
74
  "CHANGELOG.md"
75
75
  ],
76
- "gitHead": "575dbf59cb58a6c57248bf2c027a5cbc99442a63"
76
+ "gitHead": "e3ba735e1c56369bb263dd8246c711d405ed9606"
77
77
  }
@@ -4,7 +4,7 @@ var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequ
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.diff = void 0;
7
+ exports.diff = exports.assertSupportedOverlayVersion = void 0;
8
8
  var _ramda = require("ramda");
9
9
  var _apidomDatamodel = require("@speclynx/apidom-datamodel");
10
10
  var _apidomCore = require("@speclynx/apidom-core");
@@ -22,6 +22,21 @@ const defaultDiffOptions = {
22
22
  version: '0.0.0'
23
23
  }
24
24
  };
25
+
26
+ // generated actions update primitive values and concatenate arrays, which Overlay 1.0.0 doesn't allow
27
+ const supportedOverlayVersions = _apidomNsOverlay.mediaTypes.filterByFormat('generic').map(mediaType => mediaType.split('version=')[1]).filter(version => version !== '1.0.0');
28
+
29
+ /**
30
+ * Throws {@link OverlayError} when the overlay version option is unsupported.
31
+ * Shared with the URI realm, which checks it before parsing any document.
32
+ */
33
+ const assertSupportedOverlayVersion = overlay => {
34
+ const overlayVersion = overlay ?? defaultDiffOptions.overlay;
35
+ if (!supportedOverlayVersions.includes(overlayVersion)) {
36
+ throw new _OverlayError.default(`Unsupported overlay version "${overlayVersion}": diff supports ${supportedOverlayVersions.join(', ')}`);
37
+ }
38
+ };
39
+ exports.assertSupportedOverlayVersion = assertSupportedOverlayVersion;
25
40
  const createUpdateAction = (segments, value) => {
26
41
  const action = new _apidomNsOverlay.ActionElement();
27
42
  action.target = new _apidomDatamodel.StringElement(_apidomJsonPath.NormalizedPath.from(segments));
@@ -41,14 +56,19 @@ const createRemoveAction = segments => {
41
56
  return action;
42
57
  };
43
58
 
44
- // elements are compared by their serialized values, which tell an empty YAML node (null) from an
45
- // empty string; Element#equals compares the elements' own values, where both are an empty string
59
+ // elements are compared by their values, read with the core toValue
46
60
  const isEqual = (left, right) => (0, _ramda.equals)((0, _apidomCore.toValue)(left), (0, _apidomCore.toValue)(right));
61
+
62
+ // a null (as an empty YAML node is) filled in with a scalar, or a scalar emptied to a null, is
63
+ // updated in place like a value, keeping its position; any other change of type is structural
64
+ const isStructuralChange = (left, right) => {
65
+ const kinds = [left.primitive(), right.primitive()];
66
+ const isCollection = kinds.some(kind => kind === 'object' || kind === 'array');
67
+ return kinds[0] !== kinds[1] && (isCollection || !kinds.includes('null'));
68
+ };
47
69
  const diffElements = (left, right, segments, actions) => {
48
70
  if (isEqual(left, right)) return;
49
- const leftKind = left.primitive();
50
- const rightKind = right.primitive();
51
- if (leftKind !== rightKind) {
71
+ if (isStructuralChange(left, right)) {
52
72
  if (segments.length === 0) {
53
73
  throw new _OverlayError.default('Cannot diff documents with incompatible root types');
54
74
  }
@@ -111,7 +131,7 @@ const diffArrays = (left, right, segments, actions) => {
111
131
  const leftItem = left.get(i);
112
132
  const rightItem = right.get(i);
113
133
  if (isEqual(leftItem, rightItem)) continue;
114
- if (leftItem.primitive() !== rightItem.primitive()) {
134
+ if (isStructuralChange(leftItem, rightItem)) {
115
135
  // structural type change at index i — reconstruct tail:
116
136
  // remove from end down to i (reverse order preserves index stability),
117
137
  // then batch-append right[i..] as a single ArrayElement (Overlay concat semantics)
@@ -166,6 +186,7 @@ const diff = (leftElement, rightElement, options = {}) => {
166
186
  const infoTitle = options.info?.title ?? defaultDiffOptions.info.title;
167
187
  const infoVersion = options.info?.version ?? defaultDiffOptions.info.version;
168
188
  const infoDescription = options.info?.description;
189
+ assertSupportedOverlayVersion(overlayVersion);
169
190
  const actions = [];
170
191
  diffElements(leftElement, rightElement, [], actions);
171
192
  if (actions.length === 0 && options.onEmptyDiff === 'throw') {
@@ -2,7 +2,7 @@ import { equals } from 'ramda';
2
2
  import { ObjectElement, ArrayElement, StringElement, BooleanElement, isObjectElement, isArrayElement, cloneDeep } from '@speclynx/apidom-datamodel';
3
3
  import { toValue } from '@speclynx/apidom-core';
4
4
  import { NormalizedPath } from '@speclynx/apidom-json-path';
5
- import { ActionElement, Overlay1Element, InfoElement, OverlayElement, ActionsElement } from '@speclynx/apidom-ns-overlay-1';
5
+ import { ActionElement, Overlay1Element, InfoElement, OverlayElement, ActionsElement, mediaTypes } from '@speclynx/apidom-ns-overlay-1';
6
6
  import OverlayError from "../../errors/OverlayError.mjs";
7
7
  /**
8
8
  * @public
@@ -14,6 +14,20 @@ const defaultDiffOptions = {
14
14
  version: '0.0.0'
15
15
  }
16
16
  };
17
+
18
+ // generated actions update primitive values and concatenate arrays, which Overlay 1.0.0 doesn't allow
19
+ const supportedOverlayVersions = mediaTypes.filterByFormat('generic').map(mediaType => mediaType.split('version=')[1]).filter(version => version !== '1.0.0');
20
+
21
+ /**
22
+ * Throws {@link OverlayError} when the overlay version option is unsupported.
23
+ * Shared with the URI realm, which checks it before parsing any document.
24
+ */
25
+ export const assertSupportedOverlayVersion = overlay => {
26
+ const overlayVersion = overlay ?? defaultDiffOptions.overlay;
27
+ if (!supportedOverlayVersions.includes(overlayVersion)) {
28
+ throw new OverlayError(`Unsupported overlay version "${overlayVersion}": diff supports ${supportedOverlayVersions.join(', ')}`);
29
+ }
30
+ };
17
31
  const createUpdateAction = (segments, value) => {
18
32
  const action = new ActionElement();
19
33
  action.target = new StringElement(NormalizedPath.from(segments));
@@ -33,14 +47,19 @@ const createRemoveAction = segments => {
33
47
  return action;
34
48
  };
35
49
 
36
- // elements are compared by their serialized values, which tell an empty YAML node (null) from an
37
- // empty string; Element#equals compares the elements' own values, where both are an empty string
50
+ // elements are compared by their values, read with the core toValue
38
51
  const isEqual = (left, right) => equals(toValue(left), toValue(right));
52
+
53
+ // a null (as an empty YAML node is) filled in with a scalar, or a scalar emptied to a null, is
54
+ // updated in place like a value, keeping its position; any other change of type is structural
55
+ const isStructuralChange = (left, right) => {
56
+ const kinds = [left.primitive(), right.primitive()];
57
+ const isCollection = kinds.some(kind => kind === 'object' || kind === 'array');
58
+ return kinds[0] !== kinds[1] && (isCollection || !kinds.includes('null'));
59
+ };
39
60
  const diffElements = (left, right, segments, actions) => {
40
61
  if (isEqual(left, right)) return;
41
- const leftKind = left.primitive();
42
- const rightKind = right.primitive();
43
- if (leftKind !== rightKind) {
62
+ if (isStructuralChange(left, right)) {
44
63
  if (segments.length === 0) {
45
64
  throw new OverlayError('Cannot diff documents with incompatible root types');
46
65
  }
@@ -103,7 +122,7 @@ const diffArrays = (left, right, segments, actions) => {
103
122
  const leftItem = left.get(i);
104
123
  const rightItem = right.get(i);
105
124
  if (isEqual(leftItem, rightItem)) continue;
106
- if (leftItem.primitive() !== rightItem.primitive()) {
125
+ if (isStructuralChange(leftItem, rightItem)) {
107
126
  // structural type change at index i — reconstruct tail:
108
127
  // remove from end down to i (reverse order preserves index stability),
109
128
  // then batch-append right[i..] as a single ArrayElement (Overlay concat semantics)
@@ -158,6 +177,7 @@ export const diff = (leftElement, rightElement, options = {}) => {
158
177
  const infoTitle = options.info?.title ?? defaultDiffOptions.info.title;
159
178
  const infoVersion = options.info?.version ?? defaultDiffOptions.info.version;
160
179
  const infoDescription = options.info?.description;
180
+ assertSupportedOverlayVersion(overlayVersion);
161
181
  const actions = [];
162
182
  diffElements(leftElement, rightElement, [], actions);
163
183
  if (actions.length === 0 && options.onEmptyDiff === 'throw') {
@@ -49,6 +49,8 @@ const defaultOptions = exports.defaultOptions = {
49
49
  * @public
50
50
  */
51
51
  const diff = async (leftURI, rightURI, options = defaultOptions) => {
52
+ // fail on an unsupported version before fetching and parsing the documents
53
+ (0, _apidom.assertSupportedOverlayVersion)(options.overlay);
52
54
  const mergedRefOptions = (0, _apidomReference.mergeOptions)(defaultReferenceOptions, options.reference ?? {});
53
55
  const [leftParseResult, rightParseResult] = await Promise.all([(0, _apidomReference.parse)(leftURI, mergedRefOptions), (0, _apidomReference.parse)(rightURI, mergedRefOptions)]);
54
56
  const leftElement = leftParseResult.api;
@@ -1,5 +1,5 @@
1
1
  import { parse, mergeOptions } from '@speclynx/apidom-reference';
2
- import { diff as diffApiDOM } from "./apidom.mjs";
2
+ import { diff as diffApiDOM, assertSupportedOverlayVersion } from "./apidom.mjs";
3
3
  import OverlayError from "../../errors/OverlayError.mjs";
4
4
  /**
5
5
  * @public
@@ -41,6 +41,8 @@ export const defaultOptions = {
41
41
  * @public
42
42
  */
43
43
  const diff = async (leftURI, rightURI, options = defaultOptions) => {
44
+ // fail on an unsupported version before fetching and parsing the documents
45
+ assertSupportedOverlayVersion(options.overlay);
44
46
  const mergedRefOptions = mergeOptions(defaultReferenceOptions, options.reference ?? {});
45
47
  const [leftParseResult, rightParseResult] = await Promise.all([parse(leftURI, mergedRefOptions), parse(rightURI, mergedRefOptions)]);
46
48
  const leftElement = leftParseResult.api;
@@ -155,6 +155,11 @@ export declare const diffApiDOM: (leftElement: Element_2, rightElement: Element_
155
155
  * @public
156
156
  */
157
157
  export declare interface DiffOptions {
158
+ /**
159
+ * Version written to the `overlay` field (default `'1.2.0'`). Must be `1.1.0` or later,
160
+ * otherwise {@link OverlayError} is thrown: generated actions update primitive values and
161
+ * concatenate arrays, which Overlay 1.0.0 doesn't allow.
162
+ */
158
163
  readonly overlay?: string;
159
164
  readonly info?: {
160
165
  title?: string;