@speclynx/apidom-overlay 4.9.1 → 4.10.1

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/CHANGELOG.md CHANGED
@@ -3,6 +3,16 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [4.10.1](https://github.com/speclynx/apidom/compare/v4.10.0...v4.10.1) (2026-05-20)
7
+
8
+ **Note:** Version bump only for package @speclynx/apidom-overlay
9
+
10
+ # [4.10.0](https://github.com/speclynx/apidom/compare/v4.9.1...v4.10.0) (2026-05-12)
11
+
12
+ ### Features
13
+
14
+ - **overlay:** add support for overlay generation by diffing API docs ([#278](https://github.com/speclynx/apidom/issues/278)) ([41fca34](https://github.com/speclynx/apidom/commit/41fca3455bce2e637b1e53f78f3f60afd93ef16c))
15
+
6
16
  ## [4.9.1](https://github.com/speclynx/apidom/compare/v4.9.0...v4.9.1) (2026-04-21)
7
17
 
8
18
  **Note:** Version bump only for package @speclynx/apidom-overlay
package/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # @speclynx/apidom-overlay
2
2
 
3
- `@speclynx/apidom-overlay` applies [Overlay 1.x.y](https://spec.openapis.org/overlay/v1.1.0.html) documents to API definitions.
3
+ `@speclynx/apidom-overlay` applies [Overlay 1.x.y](https://spec.openapis.org/overlay/v1.1.0.html) documents to API definitions
4
+ and can generate overlays by diffing two API documents.
4
5
  The [Overlay Specification](https://spec.openapis.org/overlay/v1.1.0.html) defines a mechanism for modifying
5
6
  existing API documents without directly editing the original, using [JSONPath (RFC 9535)](https://www.rfc-editor.org/rfc/rfc9535.html)
6
7
  expressions to target specific nodes and apply updates, copies, or removals.
@@ -15,8 +16,6 @@ You can install this package via [npm CLI](https://docs.npmjs.com/cli) by runnin
15
16
 
16
17
  ## Usage
17
18
 
18
- This package provides three levels of API for applying overlays:
19
-
20
19
  ### Applying from file/URL
21
20
 
22
21
  `applyOverlay` parses an overlay document, resolves the target (from `targetURI` or the overlay's `extends` field),
@@ -109,6 +108,101 @@ const updated = applyActionPOJO(
109
108
  );
110
109
  ```
111
110
 
111
+ ### Generating an Overlay (diff)
112
+
113
+ `diffApiDOM` computes the diff between two ApiDOM elements and returns an `Overlay1Element` that,
114
+ when applied to the left document, yields the right document.
115
+
116
+ ```js
117
+ import { refract } from '@speclynx/apidom-datamodel';
118
+ import { toValue } from '@speclynx/apidom-core';
119
+ import { diffApiDOM, applyOverlayApiDOM } from '@speclynx/apidom-overlay';
120
+
121
+ const left = refract({
122
+ openapi: '3.1.0',
123
+ info: { title: 'My API', version: '1.0.0' },
124
+ servers: [{ url: 'https://staging.example.com' }],
125
+ });
126
+ const right = refract({
127
+ openapi: '3.1.0',
128
+ info: { title: 'My API', version: '2.0.0' },
129
+ servers: [{ url: 'https://prod.example.com' }, { url: 'https://dr.example.com' }],
130
+ });
131
+
132
+ const overlay = diffApiDOM(left, right, {
133
+ info: { title: 'v1 → v2 migration', version: '1.0.0' },
134
+ extends: 'https://example.com/openapi-v1.yaml',
135
+ });
136
+
137
+ // overlay is an Overlay1Element — serialize or apply it directly
138
+ const updated = applyOverlayApiDOM(overlay, left);
139
+ console.log(toValue(updated)); // equals toValue(right)
140
+ ```
141
+
142
+ `diffPOJO` accepts and returns plain JavaScript objects:
143
+
144
+ ```js
145
+ import { diffPOJO } from '@speclynx/apidom-overlay';
146
+
147
+ const overlay = diffPOJO(
148
+ { info: { title: 'Old', version: '1.0.0' } },
149
+ { info: { title: 'New', version: '2.0.0' } },
150
+ );
151
+ // overlay is a plain object: { overlay: '1.1.0', info: {...}, actions: [...] }
152
+ ```
153
+
154
+ `diffOverlay` parses two documents from file paths or URLs and returns an `Overlay1Element`.
155
+ The `extends` field is automatically set to `leftURI` when not provided, so the overlay
156
+ can be applied back to the original document without additional configuration.
157
+
158
+ ```js
159
+ import { diffOverlay } from '@speclynx/apidom-overlay';
160
+ import { toValue } from '@speclynx/apidom-core';
161
+
162
+ // diff two local files
163
+ const overlay = await diffOverlay('/path/to/openapi-v1.yaml', '/path/to/openapi-v2.yaml');
164
+
165
+ // extends is auto-populated with leftURI
166
+ console.log(toValue(overlay).extends); // '/path/to/openapi-v1.yaml'
167
+
168
+ // provide a canonical URL instead
169
+ const overlay = await diffOverlay(
170
+ '/path/to/openapi-v1.yaml',
171
+ '/path/to/openapi-v2.yaml',
172
+ { extends: 'https://example.com/openapi-v1.yaml' },
173
+ );
174
+ ```
175
+
176
+ #### DiffOptions
177
+
178
+ | Option | Type | Default | Description |
179
+ |---|---|---|---|
180
+ | `overlay` | `string` | `'1.1.0'` | Version string written to the `overlay` field. Configurable because 1.0.0 and 1.1.0 have slightly different semantics. |
181
+ | `info.title` | `string` | `'API diff'` | Title for the generated overlay's `info` object. |
182
+ | `info.version` | `string` | `'0.0.0'` | Version for the generated overlay's `info` object. |
183
+ | `info.description` | `string` | — | Optional description for the overlay's `info` object. |
184
+ | `extends` | `string` | — | When provided, sets the `extends` field on the overlay pointing to the base document. |
185
+ | `onEmptyDiff` | `'allow' \| 'throw'` | `'allow'` | Controls behavior when the documents are identical and the diff produces no actions. `'allow'` returns an overlay with an empty `actions` array; `'throw'` throws `OverlayError`. |
186
+
187
+ #### Diff algorithm
188
+
189
+ The diff walks both element trees recursively and generates [JSONPath (RFC 9535)](https://www.rfc-editor.org/rfc/rfc9535.html) targets using the normalized path format (e.g. `$['info']['title']`).
190
+
191
+ - **Objects**: per-field comparison. Removed fields produce `remove` actions. Newly added fields are batched into a single `update` on the parent object. Changed fields recurse deeper.
192
+ - **Arrays**: positional comparison. Changed items at the same index recurse deeper. New items at the end are appended via `update` on the array. Removed trailing items use `remove` in reverse-index order for index stability. Structural type changes at a given index trigger a tail-reconstruct (remove tail + re-append from right) to stay within Overlay's supported operations.
193
+ - **Primitives**: direct `update` with the new value.
194
+
195
+ #### Known limitations
196
+
197
+ | Limitation | Details |
198
+ |---|---|
199
+ | Empty actions on identical documents | By default, identical inputs produce an overlay with an empty `actions` array (Overlay 1.1.0 §3 requires non-empty). Use `onEmptyDiff: 'throw'` to turn this into an `OverlayError` instead. |
200
+ | Root structural type change | Throws `OverlayError`. Overlay has no mechanism to replace the root node. |
201
+ | Array insert-at-position | Overlay 1.x has no insert primitive. Positional diffing cascades replacements + appends, which is always correct but may be verbose for large array reorderings. |
202
+ | Array item structural type change | Handled via tail-reconstruct: items from the first type-mismatched index onward are removed and re-appended. Always correct, but produces more actions than a minimal diff. |
203
+ | Shared element references | Action `update` values reference elements from the right document directly. Mutating the right document after calling `diff` will affect the returned overlay. |
204
+ | Reverse diff | The overlay carries no "before" value. It cannot be reversed without the original document. |
205
+
112
206
  ## Overlay spec semantics
113
207
 
114
208
  The implementation follows [Overlay 1.1.0](https://spec.openapis.org/overlay/v1.1.0.html) merge rules:
@@ -197,6 +291,17 @@ Passed to `applyActionApiDOM`, `applyOverlayApiDOM`, and `applyOverlay`:
197
291
  Extends `ApplyOptions` with all [`@speclynx/apidom-reference` options](https://github.com/speclynx/apidom/blob/main/packages/apidom-reference/README.md)
198
292
  for controlling parsing, resolving, and dereferencing of the overlay and target documents.
199
293
 
294
+ ### DiffOverlayOptions
295
+
296
+ Passed to `diffOverlay`. Extends `DiffOptions` with a `reference` field:
297
+
298
+ | Option | Type | Default | Description |
299
+ |---|---|---|---|
300
+ | `overlay` | `string` | `'1.1.0'` | Inherited from `DiffOptions`. See [DiffOptions](#diffoptions). |
301
+ | `info` | `object` | — | Inherited from `DiffOptions`. |
302
+ | `extends` | `string` | `leftURI` | When omitted, automatically set to `leftURI` so the overlay points back to the base document. |
303
+ | `reference` | `PartialDeep<ReferenceOptions>` | — | [`@speclynx/apidom-reference` options](https://github.com/speclynx/apidom/blob/main/packages/apidom-reference/README.md) for parsing the left and right documents. |
304
+
200
305
  ## Validation
201
306
 
202
307
  `validateAction` checks an `ActionElement` for spec conformance before applying: