@speclynx/apidom-overlay 4.3.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/LICENSES/AFL-3.0.txt +182 -0
- package/LICENSES/Apache-2.0.txt +202 -0
- package/LICENSES/BSD-3-Clause.txt +26 -0
- package/LICENSES/MIT.txt +9 -0
- package/NOTICE +74 -0
- package/README.md +150 -0
- package/dist/apidom-overlay.browser.js +100884 -0
- package/dist/apidom-overlay.browser.min.js +1 -0
- package/package.json +73 -0
- package/src/apply-uri.cjs +80 -0
- package/src/apply-uri.mjs +74 -0
- package/src/apply.cjs +243 -0
- package/src/apply.mjs +232 -0
- package/src/errors/OverlayError.cjs +22 -0
- package/src/errors/OverlayError.mjs +19 -0
- package/src/index.cjs +14 -0
- package/src/index.mjs +4 -0
- package/src/validate.cjs +81 -0
- package/src/validate.mjs +73 -0
- package/types/apidom-overlay.d.ts +92 -0
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@speclynx/apidom-overlay",
|
|
3
|
+
"version": "4.3.1",
|
|
4
|
+
"description": "Apply Overlay 1.x.y documents to API definitions.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"apidom",
|
|
7
|
+
"api",
|
|
8
|
+
"overlay",
|
|
9
|
+
"openapi-overlay",
|
|
10
|
+
"openapi",
|
|
11
|
+
"jsonpath",
|
|
12
|
+
"rfc9535",
|
|
13
|
+
"spec",
|
|
14
|
+
"specification",
|
|
15
|
+
"typescript",
|
|
16
|
+
"javascript"
|
|
17
|
+
],
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public",
|
|
20
|
+
"registry": "https://registry.npmjs.org",
|
|
21
|
+
"provenance": false
|
|
22
|
+
},
|
|
23
|
+
"type": "module",
|
|
24
|
+
"sideEffects": false,
|
|
25
|
+
"unpkg": "./dist/apidom-overlay.browser.min.js",
|
|
26
|
+
"main": "./src/index.cjs",
|
|
27
|
+
"exports": {
|
|
28
|
+
"types": "./types/apidom-overlay.d.ts",
|
|
29
|
+
"import": "./src/index.mjs",
|
|
30
|
+
"require": "./src/index.cjs"
|
|
31
|
+
},
|
|
32
|
+
"types": "./types/apidom-overlay.d.ts",
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "npm run clean && run-p --max-parallel ${CPU_CORES:-2} typescript:declaration build:es build:cjs build:umd:browser",
|
|
35
|
+
"build:es": "cross-env BABEL_ENV=es babel src --out-dir src --extensions '.ts' --out-file-extension '.mjs' --root-mode 'upward'",
|
|
36
|
+
"build:cjs": "cross-env BABEL_ENV=cjs babel src --out-dir src --extensions '.ts' --out-file-extension '.cjs' --root-mode 'upward'",
|
|
37
|
+
"build:umd:browser": "cross-env BABEL_ENV=browser webpack --config config/webpack/browser.config.js --progress",
|
|
38
|
+
"lint": "eslint ./",
|
|
39
|
+
"lint:fix": "eslint ./ --fix",
|
|
40
|
+
"clean": "rimraf --glob 'src/**/*.mjs' 'src/**/*.cjs' 'test/**/*.mjs' ./dist ./types",
|
|
41
|
+
"test": "npm run build:es && cross-env BABEL_ENV=es babel test --out-dir test --extensions '.ts' --out-file-extension '.mjs' --root-mode 'upward' && cross-env NODE_ENV=test mocha",
|
|
42
|
+
"test:update-snapshots": "cross-env UPDATE_SNAPSHOT=1 BABEL_ENV=cjs mocha",
|
|
43
|
+
"typescript:check-types": "tsc --noEmit",
|
|
44
|
+
"typescript:declaration": "tsc -p tsconfig.declaration.json && api-extractor run -l -c ./config/api-extractor/api-extractor.json",
|
|
45
|
+
"prepack": "copyfiles -u 3 ../../LICENSES/* LICENSES && copyfiles -u 2 ../../NOTICE .",
|
|
46
|
+
"postpack": "rimraf NOTICE LICENSES"
|
|
47
|
+
},
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "git+https://github.com/speclynx/apidom.git"
|
|
51
|
+
},
|
|
52
|
+
"author": "Vladimir Gorej",
|
|
53
|
+
"license": "Apache-2.0",
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"@babel/runtime-corejs3": "^7.28.4",
|
|
56
|
+
"@speclynx/apidom-core": "4.3.1",
|
|
57
|
+
"@speclynx/apidom-datamodel": "4.3.1",
|
|
58
|
+
"@speclynx/apidom-error": "4.3.1",
|
|
59
|
+
"@speclynx/apidom-json-path": "4.3.1",
|
|
60
|
+
"@speclynx/apidom-ns-overlay-1": "4.3.1",
|
|
61
|
+
"@speclynx/apidom-reference": "4.3.1"
|
|
62
|
+
},
|
|
63
|
+
"files": [
|
|
64
|
+
"src/**/*.mjs",
|
|
65
|
+
"src/**/*.cjs",
|
|
66
|
+
"dist/",
|
|
67
|
+
"types/apidom-overlay.d.ts",
|
|
68
|
+
"LICENSES",
|
|
69
|
+
"NOTICE",
|
|
70
|
+
"README.md",
|
|
71
|
+
"CHANGELOG.md"
|
|
72
|
+
]
|
|
73
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault").default;
|
|
4
|
+
exports.__esModule = true;
|
|
5
|
+
exports.default = void 0;
|
|
6
|
+
var _apidomDatamodel = require("@speclynx/apidom-datamodel");
|
|
7
|
+
var _apidomNsOverlay = require("@speclynx/apidom-ns-overlay-1");
|
|
8
|
+
var _apidomReference = require("@speclynx/apidom-reference");
|
|
9
|
+
var _apply = require("./apply.cjs");
|
|
10
|
+
var _OverlayError = _interopRequireDefault(require("./errors/OverlayError.cjs"));
|
|
11
|
+
/**
|
|
12
|
+
* @public
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const defaultOptions = {
|
|
16
|
+
parse: {
|
|
17
|
+
parserOpts: {
|
|
18
|
+
strict: false,
|
|
19
|
+
style: true,
|
|
20
|
+
'overlay-json-1': {
|
|
21
|
+
sourceMap: true,
|
|
22
|
+
style: false,
|
|
23
|
+
extends: true
|
|
24
|
+
},
|
|
25
|
+
'overlay-yaml-1': {
|
|
26
|
+
sourceMap: true,
|
|
27
|
+
style: false,
|
|
28
|
+
extends: true
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
resolve: {
|
|
33
|
+
resolverOpts: {
|
|
34
|
+
fileAllowList: ['*']
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Applies an overlay document from a file/URL to its target document.
|
|
41
|
+
*
|
|
42
|
+
* Parses the overlay, resolves the target from `targetURI` or the overlay's
|
|
43
|
+
* `extends` field, parses the target with style preservation for round-trip fidelity,
|
|
44
|
+
* and applies all overlay actions.
|
|
45
|
+
*
|
|
46
|
+
* @param overlayURI - URI to the overlay document (file path or URL)
|
|
47
|
+
* @param targetURI - URI to the target document; if omitted, uses the overlay's `extends` field
|
|
48
|
+
* @param options - apidom-reference options + overlay merge options
|
|
49
|
+
* @returns ParseResultElement with the modified target document
|
|
50
|
+
*
|
|
51
|
+
* @public
|
|
52
|
+
*/
|
|
53
|
+
const applyOverlay = async (overlayURI, targetURI, options = {
|
|
54
|
+
immutable: false,
|
|
55
|
+
strict: false
|
|
56
|
+
}) => {
|
|
57
|
+
const mergedOptions = (0, _apidomReference.mergeOptions)(defaultOptions, options);
|
|
58
|
+
|
|
59
|
+
// parse the overlay document along with document attached to `extends` field (if present)
|
|
60
|
+
const overlayParseResult = await (0, _apidomReference.parse)(overlayURI, mergedOptions);
|
|
61
|
+
const overlayElement = overlayParseResult.api;
|
|
62
|
+
if (!(0, _apidomNsOverlay.isOverlay1Element)(overlayElement)) {
|
|
63
|
+
throw new _OverlayError.default(`Failed to parse overlay document "${overlayURI}"`);
|
|
64
|
+
}
|
|
65
|
+
let targetParseResult;
|
|
66
|
+
if (targetURI) {
|
|
67
|
+
const targetOptions = (0, _apidomReference.mergeOptions)(defaultOptions, options);
|
|
68
|
+
targetParseResult = await (0, _apidomReference.parse)(targetURI, targetOptions);
|
|
69
|
+
} else {
|
|
70
|
+
const extendsElement = overlayElement.get('extends');
|
|
71
|
+
targetParseResult = extendsElement?.meta?.get('parseResult');
|
|
72
|
+
}
|
|
73
|
+
if (!(0, _apidomDatamodel.isParseResultElement)(targetParseResult)) {
|
|
74
|
+
throw new _OverlayError.default('No target specified: provide targetURI or set the "extends" field in the overlay document');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// apply overlay actions to the target (returns targetParseResult with result replaced)
|
|
78
|
+
return (0, _apply.applyOverlayApiDOM)(overlayParseResult, targetParseResult, options);
|
|
79
|
+
};
|
|
80
|
+
var _default = exports.default = applyOverlay;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { isParseResultElement } from '@speclynx/apidom-datamodel';
|
|
2
|
+
import { isOverlay1Element } from '@speclynx/apidom-ns-overlay-1';
|
|
3
|
+
import { parse, mergeOptions } from '@speclynx/apidom-reference';
|
|
4
|
+
import { applyOverlayApiDOM } from "./apply.mjs";
|
|
5
|
+
import OverlayError from "./errors/OverlayError.mjs";
|
|
6
|
+
/**
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
9
|
+
const defaultOptions = {
|
|
10
|
+
parse: {
|
|
11
|
+
parserOpts: {
|
|
12
|
+
strict: false,
|
|
13
|
+
style: true,
|
|
14
|
+
'overlay-json-1': {
|
|
15
|
+
sourceMap: true,
|
|
16
|
+
style: false,
|
|
17
|
+
extends: true
|
|
18
|
+
},
|
|
19
|
+
'overlay-yaml-1': {
|
|
20
|
+
sourceMap: true,
|
|
21
|
+
style: false,
|
|
22
|
+
extends: true
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
resolve: {
|
|
27
|
+
resolverOpts: {
|
|
28
|
+
fileAllowList: ['*']
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Applies an overlay document from a file/URL to its target document.
|
|
35
|
+
*
|
|
36
|
+
* Parses the overlay, resolves the target from `targetURI` or the overlay's
|
|
37
|
+
* `extends` field, parses the target with style preservation for round-trip fidelity,
|
|
38
|
+
* and applies all overlay actions.
|
|
39
|
+
*
|
|
40
|
+
* @param overlayURI - URI to the overlay document (file path or URL)
|
|
41
|
+
* @param targetURI - URI to the target document; if omitted, uses the overlay's `extends` field
|
|
42
|
+
* @param options - apidom-reference options + overlay merge options
|
|
43
|
+
* @returns ParseResultElement with the modified target document
|
|
44
|
+
*
|
|
45
|
+
* @public
|
|
46
|
+
*/
|
|
47
|
+
const applyOverlay = async (overlayURI, targetURI, options = {
|
|
48
|
+
immutable: false,
|
|
49
|
+
strict: false
|
|
50
|
+
}) => {
|
|
51
|
+
const mergedOptions = mergeOptions(defaultOptions, options);
|
|
52
|
+
|
|
53
|
+
// parse the overlay document along with document attached to `extends` field (if present)
|
|
54
|
+
const overlayParseResult = await parse(overlayURI, mergedOptions);
|
|
55
|
+
const overlayElement = overlayParseResult.api;
|
|
56
|
+
if (!isOverlay1Element(overlayElement)) {
|
|
57
|
+
throw new OverlayError(`Failed to parse overlay document "${overlayURI}"`);
|
|
58
|
+
}
|
|
59
|
+
let targetParseResult;
|
|
60
|
+
if (targetURI) {
|
|
61
|
+
const targetOptions = mergeOptions(defaultOptions, options);
|
|
62
|
+
targetParseResult = await parse(targetURI, targetOptions);
|
|
63
|
+
} else {
|
|
64
|
+
const extendsElement = overlayElement.get('extends');
|
|
65
|
+
targetParseResult = extendsElement?.meta?.get('parseResult');
|
|
66
|
+
}
|
|
67
|
+
if (!isParseResultElement(targetParseResult)) {
|
|
68
|
+
throw new OverlayError('No target specified: provide targetURI or set the "extends" field in the overlay document');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// apply overlay actions to the target (returns targetParseResult with result replaced)
|
|
72
|
+
return applyOverlayApiDOM(overlayParseResult, targetParseResult, options);
|
|
73
|
+
};
|
|
74
|
+
export default applyOverlay;
|
package/src/apply.cjs
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault").default;
|
|
4
|
+
exports.__esModule = true;
|
|
5
|
+
exports.applyUpdateAction = exports.applyRemoveAction = exports.applyOverlayApiDOM = exports.applyCopyAction = exports.applyAction = void 0;
|
|
6
|
+
var _apidomDatamodel = require("@speclynx/apidom-datamodel");
|
|
7
|
+
var _apidomCore = require("@speclynx/apidom-core");
|
|
8
|
+
var _apidomJsonPath = require("@speclynx/apidom-json-path");
|
|
9
|
+
var _apidomNsOverlay = require("@speclynx/apidom-ns-overlay-1");
|
|
10
|
+
var _OverlayError = _interopRequireDefault(require("./errors/OverlayError.cjs"));
|
|
11
|
+
var _validate = require("./validate.cjs");
|
|
12
|
+
/**
|
|
13
|
+
* @public
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const defaultApplyOptions = {
|
|
17
|
+
immutable: true,
|
|
18
|
+
strict: false
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Default customMerge that enforces Overlay spec type compatibility.
|
|
23
|
+
* Throws OverlayError on incompatible type combinations.
|
|
24
|
+
*/
|
|
25
|
+
const overlayCustomMerge = (_keyElement, options) => {
|
|
26
|
+
return (target, source) => {
|
|
27
|
+
const targetIsObject = (0, _apidomDatamodel.isObjectElement)(target);
|
|
28
|
+
const sourceIsObject = (0, _apidomDatamodel.isObjectElement)(source);
|
|
29
|
+
const targetIsArray = (0, _apidomDatamodel.isArrayElement)(target);
|
|
30
|
+
const sourceIsArray = (0, _apidomDatamodel.isArrayElement)(source);
|
|
31
|
+
if (targetIsObject !== sourceIsObject || targetIsArray !== sourceIsArray) {
|
|
32
|
+
throw new _OverlayError.default(`Incompatible types in overlay merge: cannot merge ` + `${targetIsObject ? 'object' : targetIsArray ? 'array' : 'primitive'} with ` + `${sourceIsObject ? 'object' : sourceIsArray ? 'array' : 'primitive'}`);
|
|
33
|
+
}
|
|
34
|
+
return (0, _apidomCore.deepmerge)(target, source, options);
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Merges a value into a target node following Overlay spec semantics.
|
|
40
|
+
*/
|
|
41
|
+
const mergeValue = (targetElement, sourceElement, options = defaultApplyOptions) => {
|
|
42
|
+
const targetIsObject = (0, _apidomDatamodel.isObjectElement)(targetElement);
|
|
43
|
+
const targetIsArray = (0, _apidomDatamodel.isArrayElement)(targetElement);
|
|
44
|
+
const sourceIsObject = (0, _apidomDatamodel.isObjectElement)(sourceElement);
|
|
45
|
+
const sourceIsArray = (0, _apidomDatamodel.isArrayElement)(sourceElement);
|
|
46
|
+
const clone = options.immutable;
|
|
47
|
+
if (targetIsObject && sourceIsObject || targetIsArray && sourceIsArray) {
|
|
48
|
+
const mergeOpts = {
|
|
49
|
+
clone,
|
|
50
|
+
customMerge: overlayCustomMerge,
|
|
51
|
+
...options.deepmerge
|
|
52
|
+
};
|
|
53
|
+
return (0, _apidomCore.deepmerge)(targetElement, sourceElement, mergeOpts);
|
|
54
|
+
}
|
|
55
|
+
if (!targetIsObject && !targetIsArray && !sourceIsObject && !sourceIsArray) {
|
|
56
|
+
return clone ? (0, _apidomDatamodel.cloneDeep)(sourceElement) : sourceElement;
|
|
57
|
+
}
|
|
58
|
+
throw new _OverlayError.default(`Incompatible types in overlay merge: cannot merge ` + `${targetIsObject ? 'object' : targetIsArray ? 'array' : 'primitive'} with ` + `${sourceIsObject ? 'object' : sourceIsArray ? 'array' : 'primitive'}`);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Resolves a normalized path to its parent element and child key.
|
|
63
|
+
*/
|
|
64
|
+
const resolveParent = (normalizedPath, root) => {
|
|
65
|
+
const selectors = _apidomJsonPath.NormalizedPath.to(normalizedPath);
|
|
66
|
+
if (selectors.length === 0) return null; // root — no parent
|
|
67
|
+
const key = selectors[selectors.length - 1];
|
|
68
|
+
const parentPath = _apidomJsonPath.NormalizedPath.from(selectors.slice(0, -1));
|
|
69
|
+
const [parent] = (0, _apidomJsonPath.evaluate)(root, parentPath);
|
|
70
|
+
return {
|
|
71
|
+
parent,
|
|
72
|
+
key
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Applies an update action to matched nodes.
|
|
78
|
+
* @public
|
|
79
|
+
*/
|
|
80
|
+
const applyUpdateAction = (normalizedPaths, updateValue, targetElement, options = defaultApplyOptions) => {
|
|
81
|
+
let result = options.immutable ? (0, _apidomDatamodel.cloneDeep)(targetElement) : targetElement;
|
|
82
|
+
for (const normalizedPath of normalizedPaths) {
|
|
83
|
+
const resolved = resolveParent(normalizedPath, result);
|
|
84
|
+
if (resolved === null) {
|
|
85
|
+
// targeting root
|
|
86
|
+
result = mergeValue(result, updateValue, options);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const {
|
|
90
|
+
parent,
|
|
91
|
+
key
|
|
92
|
+
} = resolved;
|
|
93
|
+
if ((0, _apidomDatamodel.isObjectElement)(parent)) {
|
|
94
|
+
const current = parent.get(key);
|
|
95
|
+
if (current !== undefined) {
|
|
96
|
+
parent.set(key, mergeValue(current, updateValue, options));
|
|
97
|
+
}
|
|
98
|
+
} else if ((0, _apidomDatamodel.isArrayElement)(parent)) {
|
|
99
|
+
const current = parent.get(key);
|
|
100
|
+
if (current !== undefined) {
|
|
101
|
+
parent.content[key] = mergeValue(current, updateValue, options);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return result;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Applies a copy action to matched nodes.
|
|
110
|
+
* @public
|
|
111
|
+
*/
|
|
112
|
+
exports.applyUpdateAction = applyUpdateAction;
|
|
113
|
+
const applyCopyAction = (normalizedPaths, copyExpression, targetElement, options = defaultApplyOptions) => {
|
|
114
|
+
const sourceMatches = (0, _apidomJsonPath.evaluate)(targetElement, copyExpression);
|
|
115
|
+
if (sourceMatches.length === 0) {
|
|
116
|
+
throw new _OverlayError.default(`Copy source JSONPath "${copyExpression}" selected zero nodes`);
|
|
117
|
+
}
|
|
118
|
+
if (sourceMatches.length > 1) {
|
|
119
|
+
throw new _OverlayError.default(`Copy source JSONPath "${copyExpression}" must select a single node, but selected ${sourceMatches.length}`);
|
|
120
|
+
}
|
|
121
|
+
const sourceValue = sourceMatches[0];
|
|
122
|
+
return applyUpdateAction(normalizedPaths, sourceValue, targetElement, options);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Applies a remove action to matched nodes.
|
|
127
|
+
* @public
|
|
128
|
+
*/
|
|
129
|
+
exports.applyCopyAction = applyCopyAction;
|
|
130
|
+
const applyRemoveAction = (normalizedPaths, targetElement, options = defaultApplyOptions) => {
|
|
131
|
+
const result = options.immutable ? (0, _apidomDatamodel.cloneDeep)(targetElement) : targetElement;
|
|
132
|
+
// reverse document order so higher array indices are removed first (avoids index shifting)
|
|
133
|
+
const sorted = [...normalizedPaths].reverse();
|
|
134
|
+
for (const normalizedPath of sorted) {
|
|
135
|
+
const resolved = resolveParent(normalizedPath, result);
|
|
136
|
+
if (resolved === null) {
|
|
137
|
+
throw new _OverlayError.default('Cannot remove the root node');
|
|
138
|
+
}
|
|
139
|
+
const {
|
|
140
|
+
parent,
|
|
141
|
+
key
|
|
142
|
+
} = resolved;
|
|
143
|
+
if ((0, _apidomDatamodel.isObjectElement)(parent)) {
|
|
144
|
+
parent.remove(key);
|
|
145
|
+
} else if ((0, _apidomDatamodel.isArrayElement)(parent)) {
|
|
146
|
+
parent.content.splice(key, 1);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return result;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Applies a single overlay action to a target element.
|
|
154
|
+
* Immutable — returns a new element with the action applied.
|
|
155
|
+
*
|
|
156
|
+
* @public
|
|
157
|
+
*/
|
|
158
|
+
exports.applyRemoveAction = applyRemoveAction;
|
|
159
|
+
const applyAction = (actionElement, targetElement, options = defaultApplyOptions) => {
|
|
160
|
+
const actionValidation = (0, _validate.validateAction)(actionElement);
|
|
161
|
+
if (!actionValidation.valid) throw actionValidation.error;
|
|
162
|
+
const targetExpression = (0, _apidomCore.toValue)(actionElement.target);
|
|
163
|
+
const matches = [];
|
|
164
|
+
const normalizedPaths = [];
|
|
165
|
+
(0, _apidomJsonPath.evaluate)(targetElement, targetExpression, {
|
|
166
|
+
callback: (value, normalizedPath) => {
|
|
167
|
+
matches.push(value);
|
|
168
|
+
normalizedPaths.push(normalizedPath);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
if (matches.length === 0) {
|
|
172
|
+
if (options.strict) {
|
|
173
|
+
throw new _OverlayError.default(`Action target "${targetExpression}" matched zero nodes (strict mode)`, {
|
|
174
|
+
action: actionElement
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
return targetElement;
|
|
178
|
+
}
|
|
179
|
+
try {
|
|
180
|
+
// remove action
|
|
181
|
+
const removeValue = actionElement.removeField;
|
|
182
|
+
if (removeValue !== undefined && (0, _apidomCore.toValue)(removeValue) === true) {
|
|
183
|
+
return applyRemoveAction(normalizedPaths, targetElement, options);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// update action
|
|
187
|
+
if (actionElement.update !== undefined) {
|
|
188
|
+
const nodesValidation = (0, _validate.validateTargetNodes)(matches);
|
|
189
|
+
if (!nodesValidation.valid) throw nodesValidation.error;
|
|
190
|
+
return applyUpdateAction(normalizedPaths, actionElement.update, targetElement, options);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// copy action
|
|
194
|
+
if (actionElement.copy !== undefined) {
|
|
195
|
+
const nodesValidation = (0, _validate.validateTargetNodes)(matches);
|
|
196
|
+
if (!nodesValidation.valid) throw nodesValidation.error;
|
|
197
|
+
const copyExpression = (0, _apidomCore.toValue)(actionElement.copy);
|
|
198
|
+
return applyCopyAction(normalizedPaths, copyExpression, targetElement, options);
|
|
199
|
+
}
|
|
200
|
+
return targetElement;
|
|
201
|
+
} catch (error) {
|
|
202
|
+
// enrich OverlayError with action context, preserving original stack
|
|
203
|
+
if (error instanceof _OverlayError.default && !error.action) {
|
|
204
|
+
error.action = actionElement;
|
|
205
|
+
}
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Applies an entire overlay document to a target element.
|
|
212
|
+
* Immutable — returns a new element with all actions applied sequentially.
|
|
213
|
+
*
|
|
214
|
+
* @public
|
|
215
|
+
*/
|
|
216
|
+
exports.applyAction = applyAction;
|
|
217
|
+
const applyOverlayApiDOM = (overlayElement, targetElement, options = defaultApplyOptions) => {
|
|
218
|
+
const overlay = overlayElement instanceof _apidomDatamodel.ParseResultElement ? overlayElement.api : overlayElement;
|
|
219
|
+
if (!(0, _apidomNsOverlay.isOverlay1Element)(overlay)) {
|
|
220
|
+
throw new _OverlayError.default('First argument must be an Overlay 1.x document');
|
|
221
|
+
}
|
|
222
|
+
let target;
|
|
223
|
+
if (targetElement instanceof _apidomDatamodel.ParseResultElement) {
|
|
224
|
+
if (targetElement.api === undefined) {
|
|
225
|
+
throw new _OverlayError.default('Target ParseResultElement contains no API element');
|
|
226
|
+
}
|
|
227
|
+
target = targetElement.api;
|
|
228
|
+
} else {
|
|
229
|
+
target = targetElement;
|
|
230
|
+
}
|
|
231
|
+
if (!(0, _apidomDatamodel.isArrayElement)(overlay.actions)) {
|
|
232
|
+
return targetElement;
|
|
233
|
+
}
|
|
234
|
+
for (const action of overlay.actions) {
|
|
235
|
+
target = applyAction(action, target, options);
|
|
236
|
+
}
|
|
237
|
+
if (targetElement instanceof _apidomDatamodel.ParseResultElement) {
|
|
238
|
+
targetElement.replaceResult(target);
|
|
239
|
+
return targetElement;
|
|
240
|
+
}
|
|
241
|
+
return target;
|
|
242
|
+
};
|
|
243
|
+
exports.applyOverlayApiDOM = applyOverlayApiDOM;
|