@speclynx/apidom-core 4.0.2 → 4.0.4

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.
Files changed (50) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/package.json +5 -5
  3. package/src/fields/fixed-fields.cjs +47 -0
  4. package/src/fields/fixed-fields.mjs +43 -0
  5. package/src/fields/index.cjs +8 -0
  6. package/src/fields/index.mjs +1 -0
  7. package/src/identity/errors/ElementIdentityError.cjs +22 -0
  8. package/src/identity/errors/ElementIdentityError.mjs +19 -0
  9. package/src/identity/index.cjs +64 -0
  10. package/src/identity/index.mjs +58 -0
  11. package/src/index.cjs +48 -0
  12. package/src/index.mjs +41 -0
  13. package/src/media-types.cjs +21 -0
  14. package/src/media-types.mjs +18 -0
  15. package/src/merge/deepmerge.cjs +165 -0
  16. package/src/merge/deepmerge.mjs +149 -0
  17. package/src/merge/merge-left.cjs +16 -0
  18. package/src/merge/merge-left.mjs +11 -0
  19. package/src/merge/merge-right.cjs +35 -0
  20. package/src/merge/merge-right.mjs +29 -0
  21. package/src/namespace.cjs +10 -0
  22. package/src/namespace.mjs +7 -0
  23. package/src/refractor/plugins/dispatcher/index.cjs +64 -0
  24. package/src/refractor/plugins/dispatcher/index.mjs +54 -0
  25. package/src/refractor/plugins/element-identity.cjs +31 -0
  26. package/src/refractor/plugins/element-identity.mjs +26 -0
  27. package/src/refractor/plugins/semantic-element-identity.cjs +33 -0
  28. package/src/refractor/plugins/semantic-element-identity.mjs +29 -0
  29. package/src/refractor/toolbox.cjs +47 -0
  30. package/src/refractor/toolbox.mjs +41 -0
  31. package/src/specification.cjs +63 -0
  32. package/src/specification.mjs +59 -0
  33. package/src/transcluder/Transcluder.cjs +111 -0
  34. package/src/transcluder/Transcluder.mjs +107 -0
  35. package/src/transcluder/index.cjs +19 -0
  36. package/src/transcluder/index.mjs +13 -0
  37. package/src/transformers/dehydrate.cjs +15 -0
  38. package/src/transformers/dehydrate.mjs +10 -0
  39. package/src/transformers/from.cjs +34 -0
  40. package/src/transformers/from.mjs +29 -0
  41. package/src/transformers/serializers/json.cjs +75 -0
  42. package/src/transformers/serializers/json.mjs +70 -0
  43. package/src/transformers/serializers/value.cjs +50 -0
  44. package/src/transformers/serializers/value.mjs +47 -0
  45. package/src/transformers/serializers/yaml-1-2.cjs +142 -0
  46. package/src/transformers/serializers/yaml-1-2.mjs +137 -0
  47. package/src/transformers/sexprs.cjs +31 -0
  48. package/src/transformers/sexprs.mjs +28 -0
  49. package/src/transformers/to-string.cjs +16 -0
  50. package/src/transformers/to-string.mjs +11 -0
@@ -0,0 +1,59 @@
1
+ import { mapObjIndexed, path, has, propSatisfies } from 'ramda';
2
+ import { isPlainObject, trimCharsStart, isString } from 'ramda-adjunct';
3
+
4
+ /**
5
+ * Base type for resolved specification objects.
6
+ * Extend this in namespace packages to add specific type information.
7
+ * @public
8
+ */
9
+
10
+ const specCache = new WeakMap();
11
+
12
+ /**
13
+ * Resolves a specification object by:
14
+ * 1. Dereferencing all $ref pointers
15
+ * 2. Building elementMap from objects with `element` property
16
+ * 3. Caching results for efficiency
17
+ *
18
+ * @public
19
+ */
20
+ export const resolveSpecification = specification => {
21
+ if (specCache.has(specification)) {
22
+ return specCache.get(specification);
23
+ }
24
+ const elementMap = {};
25
+ const traverse = (obj, root, currentPath) => {
26
+ // Collect element mapping
27
+ if (typeof obj.element === 'string') {
28
+ elementMap[obj.element] = obj.$visitor ? [...currentPath, '$visitor'] : currentPath;
29
+ }
30
+ return mapObjIndexed((val, key) => {
31
+ const newPath = [...currentPath, key];
32
+ if (isPlainObject(val) && has('$ref', val) && propSatisfies(isString, '$ref', val)) {
33
+ const $ref = path(['$ref'], val);
34
+ const pointer = trimCharsStart('#/', $ref);
35
+ const resolved = path(pointer.split('/'), root);
36
+ // merge extra properties (e.g. alias) from the $ref object into the resolved value
37
+ const {
38
+ $ref: _,
39
+ ...rest
40
+ } = val;
41
+ if (Object.keys(rest).length > 0 && isPlainObject(resolved)) {
42
+ return {
43
+ ...resolved,
44
+ ...rest
45
+ };
46
+ }
47
+ return resolved;
48
+ }
49
+ if (isPlainObject(val)) {
50
+ return traverse(val, root, newPath);
51
+ }
52
+ return val;
53
+ }, obj);
54
+ };
55
+ const resolved = traverse(specification, specification, []);
56
+ resolved.elementMap = elementMap;
57
+ specCache.set(specification, resolved);
58
+ return resolved;
59
+ };
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.default = void 0;
5
+ var _apidomDatamodel = require("@speclynx/apidom-datamodel");
6
+ var _ramdaAdjunct = require("ramda-adjunct");
7
+ const computeEdges = (element, edges = new WeakMap()) => {
8
+ if ((0, _apidomDatamodel.isMemberElement)(element)) {
9
+ // @ts-ignore
10
+ edges.set(element.key, element);
11
+ // @ts-ignore
12
+ computeEdges(element.key, edges);
13
+ // @ts-ignore
14
+ edges.set(element.value, element);
15
+ // @ts-ignore
16
+ computeEdges(element.value, edges);
17
+ } else {
18
+ element.children.forEach(childElement => {
19
+ edges.set(childElement, element);
20
+ computeEdges(childElement, edges);
21
+ });
22
+ }
23
+ return edges;
24
+ };
25
+ const transcludeChildOfMemberElement = (search, replace, edges) => {
26
+ const memberElement = edges.get(search);
27
+ if (!(0, _apidomDatamodel.isMemberElement)(memberElement)) {
28
+ return;
29
+ }
30
+ if (memberElement.key === search) {
31
+ memberElement.key = replace;
32
+ edges.delete(search);
33
+ edges.set(replace, memberElement);
34
+ }
35
+ if (memberElement.value === search) {
36
+ memberElement.value = replace;
37
+ edges.delete(search);
38
+ edges.set(replace, memberElement);
39
+ }
40
+ };
41
+ const transcludeChildOfObjectElement = (search, replace, edges) => {
42
+ const objectElement = edges.get(search);
43
+ if (!(0, _apidomDatamodel.isObjectElement)(objectElement)) {
44
+ return;
45
+ }
46
+ objectElement.content = objectElement.map((value, key, member) => {
47
+ if (member === search) {
48
+ edges.delete(search);
49
+ edges.set(replace, objectElement);
50
+ return replace;
51
+ }
52
+ return member;
53
+ });
54
+ };
55
+ const transcludeChildOfArrayElement = (search, replace, edges) => {
56
+ const arrayElement = edges.get(search);
57
+ if (!(0, _apidomDatamodel.isArrayElement)(arrayElement)) {
58
+ return;
59
+ }
60
+ arrayElement.content = arrayElement.map(element => {
61
+ if (element === search) {
62
+ edges.delete(search);
63
+ edges.set(replace, arrayElement);
64
+ return replace;
65
+ }
66
+ return element;
67
+ });
68
+ };
69
+
70
+ /**
71
+ * This is a mutating stamp. If you don't want your Element to be mutated,
72
+ * clone in before passing it to initializer of this stamp.
73
+ * @public
74
+ */
75
+
76
+ class Transcluder {
77
+ element;
78
+ edges;
79
+ constructor({
80
+ element
81
+ }) {
82
+ this.element = element;
83
+ }
84
+ transclude(search, replace) {
85
+ // shortcut 1. - replacing entire ApiDOM tree
86
+ if (search === this.element) return replace;
87
+ // shortcut 2. - replacing nothing
88
+ if (search === replace) return this.element;
89
+ this.edges = this.edges ?? computeEdges(this.element);
90
+ const parent = this.edges.get(search);
91
+ if ((0, _ramdaAdjunct.isUndefined)(parent)) {
92
+ return undefined;
93
+ }
94
+
95
+ /**
96
+ * This predicate must be first because ObjectElement extends ArrayElement.
97
+ * isArrayElement returns true for ObjectElements.
98
+ * (classical problems with polymorphism)
99
+ */
100
+ if ((0, _apidomDatamodel.isObjectElement)(parent)) {
101
+ // @ts-ignore
102
+ transcludeChildOfObjectElement(search, replace, this.edges);
103
+ } else if ((0, _apidomDatamodel.isArrayElement)(parent)) {
104
+ transcludeChildOfArrayElement(search, replace, this.edges);
105
+ } else if ((0, _apidomDatamodel.isMemberElement)(parent)) {
106
+ transcludeChildOfMemberElement(search, replace, this.edges);
107
+ }
108
+ return this.element;
109
+ }
110
+ }
111
+ var _default = exports.default = Transcluder;
@@ -0,0 +1,107 @@
1
+ import { isObjectElement, isArrayElement, isMemberElement } from '@speclynx/apidom-datamodel';
2
+ import { isUndefined } from 'ramda-adjunct';
3
+ const computeEdges = (element, edges = new WeakMap()) => {
4
+ if (isMemberElement(element)) {
5
+ // @ts-ignore
6
+ edges.set(element.key, element);
7
+ // @ts-ignore
8
+ computeEdges(element.key, edges);
9
+ // @ts-ignore
10
+ edges.set(element.value, element);
11
+ // @ts-ignore
12
+ computeEdges(element.value, edges);
13
+ } else {
14
+ element.children.forEach(childElement => {
15
+ edges.set(childElement, element);
16
+ computeEdges(childElement, edges);
17
+ });
18
+ }
19
+ return edges;
20
+ };
21
+ const transcludeChildOfMemberElement = (search, replace, edges) => {
22
+ const memberElement = edges.get(search);
23
+ if (!isMemberElement(memberElement)) {
24
+ return;
25
+ }
26
+ if (memberElement.key === search) {
27
+ memberElement.key = replace;
28
+ edges.delete(search);
29
+ edges.set(replace, memberElement);
30
+ }
31
+ if (memberElement.value === search) {
32
+ memberElement.value = replace;
33
+ edges.delete(search);
34
+ edges.set(replace, memberElement);
35
+ }
36
+ };
37
+ const transcludeChildOfObjectElement = (search, replace, edges) => {
38
+ const objectElement = edges.get(search);
39
+ if (!isObjectElement(objectElement)) {
40
+ return;
41
+ }
42
+ objectElement.content = objectElement.map((value, key, member) => {
43
+ if (member === search) {
44
+ edges.delete(search);
45
+ edges.set(replace, objectElement);
46
+ return replace;
47
+ }
48
+ return member;
49
+ });
50
+ };
51
+ const transcludeChildOfArrayElement = (search, replace, edges) => {
52
+ const arrayElement = edges.get(search);
53
+ if (!isArrayElement(arrayElement)) {
54
+ return;
55
+ }
56
+ arrayElement.content = arrayElement.map(element => {
57
+ if (element === search) {
58
+ edges.delete(search);
59
+ edges.set(replace, arrayElement);
60
+ return replace;
61
+ }
62
+ return element;
63
+ });
64
+ };
65
+
66
+ /**
67
+ * This is a mutating stamp. If you don't want your Element to be mutated,
68
+ * clone in before passing it to initializer of this stamp.
69
+ * @public
70
+ */
71
+
72
+ class Transcluder {
73
+ element;
74
+ edges;
75
+ constructor({
76
+ element
77
+ }) {
78
+ this.element = element;
79
+ }
80
+ transclude(search, replace) {
81
+ // shortcut 1. - replacing entire ApiDOM tree
82
+ if (search === this.element) return replace;
83
+ // shortcut 2. - replacing nothing
84
+ if (search === replace) return this.element;
85
+ this.edges = this.edges ?? computeEdges(this.element);
86
+ const parent = this.edges.get(search);
87
+ if (isUndefined(parent)) {
88
+ return undefined;
89
+ }
90
+
91
+ /**
92
+ * This predicate must be first because ObjectElement extends ArrayElement.
93
+ * isArrayElement returns true for ObjectElements.
94
+ * (classical problems with polymorphism)
95
+ */
96
+ if (isObjectElement(parent)) {
97
+ // @ts-ignore
98
+ transcludeChildOfObjectElement(search, replace, this.edges);
99
+ } else if (isArrayElement(parent)) {
100
+ transcludeChildOfArrayElement(search, replace, this.edges);
101
+ } else if (isMemberElement(parent)) {
102
+ transcludeChildOfMemberElement(search, replace, this.edges);
103
+ }
104
+ return this.element;
105
+ }
106
+ }
107
+ export default Transcluder;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault").default;
4
+ exports.__esModule = true;
5
+ exports.transclude = exports.default = void 0;
6
+ var _Transcluder = _interopRequireDefault(require("./Transcluder.cjs"));
7
+ /**
8
+ * This is a mutating function. If you don't want your Element to be mutated,
9
+ * clone in before passing it to this function.
10
+ * @public
11
+ */
12
+ const transclude = (search, replace, element) => {
13
+ const transcluder = new _Transcluder.default({
14
+ element
15
+ });
16
+ return transcluder.transclude(search, replace);
17
+ };
18
+ exports.transclude = transclude;
19
+ var _default = exports.default = _Transcluder.default;
@@ -0,0 +1,13 @@
1
+ import Transcluder from "./Transcluder.mjs";
2
+ /**
3
+ * This is a mutating function. If you don't want your Element to be mutated,
4
+ * clone in before passing it to this function.
5
+ * @public
6
+ */
7
+ export const transclude = (search, replace, element) => {
8
+ const transcluder = new Transcluder({
9
+ element
10
+ });
11
+ return transcluder.transclude(search, replace);
12
+ };
13
+ export default Transcluder;
@@ -0,0 +1,15 @@
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 _namespace = _interopRequireDefault(require("../namespace.cjs"));
7
+ /**
8
+ * Creates a refract representation of an Element.
9
+ * https://github.com/refractproject/refract-spec
10
+ * @public
11
+ */
12
+ const dehydrate = (element, namespace = _namespace.default) => {
13
+ return namespace.toRefract(element);
14
+ };
15
+ var _default = exports.default = dehydrate;
@@ -0,0 +1,10 @@
1
+ import defaultNamespaceInstance from "../namespace.mjs";
2
+ /**
3
+ * Creates a refract representation of an Element.
4
+ * https://github.com/refractproject/refract-spec
5
+ * @public
6
+ */
7
+ const dehydrate = (element, namespace = defaultNamespaceInstance) => {
8
+ return namespace.toRefract(element);
9
+ };
10
+ export default dehydrate;
@@ -0,0 +1,34 @@
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 _ramda = require("ramda");
7
+ var _ramdaAdjunct = require("ramda-adjunct");
8
+ var _namespace = _interopRequireDefault(require("../namespace.cjs"));
9
+ /**
10
+ * Transforms data to an Element from a particular namespace.
11
+ *
12
+ * The name of the function was originally `from`,
13
+ * but it was renamed to `fromFn` to avoid issues with Parcel.js:
14
+ *
15
+ * - https://github.com/parcel-bundler/parcel/issues/9473
16
+ * - https://github.com/swagger-api/swagger-ui/issues/9466#issuecomment-1881053410
17
+ * @public
18
+ */
19
+ const fromFn = (data, namespace = _namespace.default) => {
20
+ if ((0, _ramdaAdjunct.isString)(data)) {
21
+ // JSON serialized refract
22
+ try {
23
+ return namespace.fromRefract(JSON.parse(data));
24
+ } catch {
25
+ // noop
26
+ }
27
+ }
28
+ if ((0, _ramdaAdjunct.isPlainObject)(data) && (0, _ramda.has)('element', data)) {
29
+ // refract javascript structure
30
+ return namespace.fromRefract(data);
31
+ }
32
+ return namespace.toElement(data);
33
+ };
34
+ var _default = exports.default = fromFn;
@@ -0,0 +1,29 @@
1
+ import { has } from 'ramda';
2
+ import { isPlainObject, isString } from 'ramda-adjunct';
3
+ import defaultNamespace from "../namespace.mjs";
4
+ /**
5
+ * Transforms data to an Element from a particular namespace.
6
+ *
7
+ * The name of the function was originally `from`,
8
+ * but it was renamed to `fromFn` to avoid issues with Parcel.js:
9
+ *
10
+ * - https://github.com/parcel-bundler/parcel/issues/9473
11
+ * - https://github.com/swagger-api/swagger-ui/issues/9466#issuecomment-1881053410
12
+ * @public
13
+ */
14
+ const fromFn = (data, namespace = defaultNamespace) => {
15
+ if (isString(data)) {
16
+ // JSON serialized refract
17
+ try {
18
+ return namespace.fromRefract(JSON.parse(data));
19
+ } catch {
20
+ // noop
21
+ }
22
+ }
23
+ if (isPlainObject(data) && has('element', data)) {
24
+ // refract javascript structure
25
+ return namespace.fromRefract(data);
26
+ }
27
+ return namespace.toElement(data);
28
+ };
29
+ export default fromFn;
@@ -0,0 +1,75 @@
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 _value = _interopRequireDefault(require("./value.cjs"));
8
+ const getStyle = element => {
9
+ return element.style?.json ?? {};
10
+ };
11
+
12
+ /**
13
+ * @public
14
+ */
15
+
16
+ /**
17
+ * Builds a POJO from an ApiDOM element tree. Numbers with rawContent
18
+ * are replaced with sentinel strings; all other values go through toValue().
19
+ */
20
+ const toPojo = (element, sentinels) => {
21
+ const visited = new WeakSet();
22
+ const convert = node => {
23
+ if (!(0, _apidomDatamodel.isElement)(node)) return node;
24
+ if (visited.has(node)) return null;
25
+ visited.add(node);
26
+ if ((0, _apidomDatamodel.isObjectElement)(node)) {
27
+ const obj = {};
28
+ node.forEach((value, key) => {
29
+ const k = (0, _apidomDatamodel.isElement)(key) ? (0, _value.default)(key) : key;
30
+ if (typeof k === 'string') obj[k] = convert(value);
31
+ });
32
+ return obj;
33
+ }
34
+ if ((0, _apidomDatamodel.isArrayElement)(node)) {
35
+ const arr = [];
36
+ node.forEach(item => arr.push(convert(item)));
37
+ return arr;
38
+ }
39
+ if ((0, _apidomDatamodel.isRefElement)(node)) return String((0, _value.default)(node));
40
+ if ((0, _apidomDatamodel.isLinkElement)(node)) return (0, _apidomDatamodel.isStringElement)(node.href) ? (0, _value.default)(node.href) : '';
41
+
42
+ // number with rawContent — substitute with sentinel
43
+ if ((0, _apidomDatamodel.isNumberElement)(node)) {
44
+ const style = getStyle(node);
45
+ if (typeof style.rawContent === 'string') {
46
+ const sentinel = `\0RAW${sentinels.size}\0`;
47
+ sentinels.set(sentinel, style.rawContent);
48
+ return sentinel;
49
+ }
50
+ }
51
+ return (0, _value.default)(node);
52
+ };
53
+ return convert(element);
54
+ };
55
+
56
+ /**
57
+ * @public
58
+ */
59
+ const serializer = (element, replacer, space, options) => {
60
+ if (options?.preserveStyle) {
61
+ const style = getStyle(element);
62
+ const indent = typeof space === 'number' ? space : typeof style.indent === 'number' ? style.indent : 0;
63
+ const sentinels = new Map();
64
+ const pojo = toPojo(element, sentinels);
65
+ let serialized = JSON.stringify(pojo, null, indent);
66
+
67
+ // replace quoted sentinels with raw number representations
68
+ for (const [sentinel, raw] of sentinels) {
69
+ serialized = serialized.replace(JSON.stringify(sentinel), raw);
70
+ }
71
+ return serialized;
72
+ }
73
+ return JSON.stringify((0, _value.default)(element), replacer, space);
74
+ };
75
+ var _default = exports.default = serializer;
@@ -0,0 +1,70 @@
1
+ import { isElement, isObjectElement, isArrayElement, isRefElement, isLinkElement, isStringElement, isNumberElement } from '@speclynx/apidom-datamodel';
2
+ import toValue from "./value.mjs";
3
+ const getStyle = element => {
4
+ return element.style?.json ?? {};
5
+ };
6
+
7
+ /**
8
+ * @public
9
+ */
10
+
11
+ /**
12
+ * Builds a POJO from an ApiDOM element tree. Numbers with rawContent
13
+ * are replaced with sentinel strings; all other values go through toValue().
14
+ */
15
+ const toPojo = (element, sentinels) => {
16
+ const visited = new WeakSet();
17
+ const convert = node => {
18
+ if (!isElement(node)) return node;
19
+ if (visited.has(node)) return null;
20
+ visited.add(node);
21
+ if (isObjectElement(node)) {
22
+ const obj = {};
23
+ node.forEach((value, key) => {
24
+ const k = isElement(key) ? toValue(key) : key;
25
+ if (typeof k === 'string') obj[k] = convert(value);
26
+ });
27
+ return obj;
28
+ }
29
+ if (isArrayElement(node)) {
30
+ const arr = [];
31
+ node.forEach(item => arr.push(convert(item)));
32
+ return arr;
33
+ }
34
+ if (isRefElement(node)) return String(toValue(node));
35
+ if (isLinkElement(node)) return isStringElement(node.href) ? toValue(node.href) : '';
36
+
37
+ // number with rawContent — substitute with sentinel
38
+ if (isNumberElement(node)) {
39
+ const style = getStyle(node);
40
+ if (typeof style.rawContent === 'string') {
41
+ const sentinel = `\0RAW${sentinels.size}\0`;
42
+ sentinels.set(sentinel, style.rawContent);
43
+ return sentinel;
44
+ }
45
+ }
46
+ return toValue(node);
47
+ };
48
+ return convert(element);
49
+ };
50
+
51
+ /**
52
+ * @public
53
+ */
54
+ const serializer = (element, replacer, space, options) => {
55
+ if (options?.preserveStyle) {
56
+ const style = getStyle(element);
57
+ const indent = typeof space === 'number' ? space : typeof style.indent === 'number' ? style.indent : 0;
58
+ const sentinels = new Map();
59
+ const pojo = toPojo(element, sentinels);
60
+ let serialized = JSON.stringify(pojo, null, indent);
61
+
62
+ // replace quoted sentinels with raw number representations
63
+ for (const [sentinel, raw] of sentinels) {
64
+ serialized = serialized.replace(JSON.stringify(sentinel), raw);
65
+ }
66
+ return serialized;
67
+ }
68
+ return JSON.stringify(toValue(element), replacer, space);
69
+ };
70
+ export default serializer;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.default = void 0;
5
+ var _apidomDatamodel = require("@speclynx/apidom-datamodel");
6
+ /**
7
+ * Transforms the ApiDOM into JavaScript POJO.
8
+ * This POJO would be the result of interpreting the ApiDOM
9
+ * into JavaScript structure.
10
+ * @public
11
+ */
12
+ const serializer = element => {
13
+ if (!(0, _apidomDatamodel.isElement)(element)) return element;
14
+
15
+ // Shortcut optimization for primitive element types
16
+ if ((0, _apidomDatamodel.isStringElement)(element) || (0, _apidomDatamodel.isNumberElement)(element) || (0, _apidomDatamodel.isBooleanElement)(element) || (0, _apidomDatamodel.isNullElement)(element)) {
17
+ return element.toValue();
18
+ }
19
+
20
+ // WeakMap for cycle handling - stores references to already-visited elements
21
+ const references = new WeakMap();
22
+ const serialize = node => {
23
+ if (!(0, _apidomDatamodel.isElement)(node)) return node;
24
+ if ((0, _apidomDatamodel.isObjectElement)(node)) {
25
+ if (references.has(node)) return references.get(node);
26
+ const obj = {};
27
+ references.set(node, obj);
28
+ node.forEach((value, key) => {
29
+ const k = serialize(key);
30
+ const v = serialize(value);
31
+ if (typeof k === 'string') obj[k] = v;
32
+ });
33
+ return obj;
34
+ }
35
+ if ((0, _apidomDatamodel.isArrayElement)(node)) {
36
+ if (references.has(node)) return references.get(node);
37
+ const arr = [];
38
+ references.set(node, arr);
39
+ node.forEach(item => arr.push(serialize(item)));
40
+ return arr;
41
+ }
42
+ if ((0, _apidomDatamodel.isRefElement)(node)) return String(node.toValue());
43
+ if ((0, _apidomDatamodel.isLinkElement)(node)) {
44
+ return (0, _apidomDatamodel.isStringElement)(node.href) ? node.href.toValue() : '';
45
+ }
46
+ return node.toValue();
47
+ };
48
+ return serialize(element);
49
+ };
50
+ var _default = exports.default = serializer;
@@ -0,0 +1,47 @@
1
+ import { isElement, isObjectElement, isArrayElement, isRefElement, isLinkElement, isStringElement, isBooleanElement, isNumberElement, isNullElement } from '@speclynx/apidom-datamodel';
2
+
3
+ /**
4
+ * Transforms the ApiDOM into JavaScript POJO.
5
+ * This POJO would be the result of interpreting the ApiDOM
6
+ * into JavaScript structure.
7
+ * @public
8
+ */
9
+ const serializer = element => {
10
+ if (!isElement(element)) return element;
11
+
12
+ // Shortcut optimization for primitive element types
13
+ if (isStringElement(element) || isNumberElement(element) || isBooleanElement(element) || isNullElement(element)) {
14
+ return element.toValue();
15
+ }
16
+
17
+ // WeakMap for cycle handling - stores references to already-visited elements
18
+ const references = new WeakMap();
19
+ const serialize = node => {
20
+ if (!isElement(node)) return node;
21
+ if (isObjectElement(node)) {
22
+ if (references.has(node)) return references.get(node);
23
+ const obj = {};
24
+ references.set(node, obj);
25
+ node.forEach((value, key) => {
26
+ const k = serialize(key);
27
+ const v = serialize(value);
28
+ if (typeof k === 'string') obj[k] = v;
29
+ });
30
+ return obj;
31
+ }
32
+ if (isArrayElement(node)) {
33
+ if (references.has(node)) return references.get(node);
34
+ const arr = [];
35
+ references.set(node, arr);
36
+ node.forEach(item => arr.push(serialize(item)));
37
+ return arr;
38
+ }
39
+ if (isRefElement(node)) return String(node.toValue());
40
+ if (isLinkElement(node)) {
41
+ return isStringElement(node.href) ? node.href.toValue() : '';
42
+ }
43
+ return node.toValue();
44
+ };
45
+ return serialize(element);
46
+ };
47
+ export default serializer;