@cyclonedx/cyclonedx-library 1.0.0-beta.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.
Files changed (169) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +5 -0
  3. package/README.md +152 -0
  4. package/dist.node/_index.node.js +53 -0
  5. package/dist.node/_index.node.js.map +1 -0
  6. package/dist.node/enums/attachmentEncoding.js +26 -0
  7. package/dist.node/enums/attachmentEncoding.js.map +1 -0
  8. package/dist.node/enums/componentScope.js +28 -0
  9. package/dist.node/enums/componentScope.js.map +1 -0
  10. package/dist.node/enums/componentType.js +33 -0
  11. package/dist.node/enums/componentType.js.map +1 -0
  12. package/dist.node/enums/externalReferenceType.js +41 -0
  13. package/dist.node/enums/externalReferenceType.js.map +1 -0
  14. package/dist.node/enums/hashAlogorithm.js +37 -0
  15. package/dist.node/enums/hashAlogorithm.js.map +1 -0
  16. package/dist.node/enums/index.js +40 -0
  17. package/dist.node/enums/index.js.map +1 -0
  18. package/dist.node/factories/index.js +36 -0
  19. package/dist.node/factories/index.js.map +1 -0
  20. package/dist.node/factories/licenseFactory.js +56 -0
  21. package/dist.node/factories/licenseFactory.js.map +1 -0
  22. package/dist.node/helpers/types.js +26 -0
  23. package/dist.node/helpers/types.js.map +1 -0
  24. package/dist.node/models/attachment.js +30 -0
  25. package/dist.node/models/attachment.js.map +1 -0
  26. package/dist.node/models/bom.js +67 -0
  27. package/dist.node/models/bom.js.map +1 -0
  28. package/dist.node/models/bomRef.js +37 -0
  29. package/dist.node/models/bomRef.js.map +1 -0
  30. package/dist.node/models/component.js +96 -0
  31. package/dist.node/models/component.js.map +1 -0
  32. package/dist.node/models/externalReference.js +40 -0
  33. package/dist.node/models/externalReference.js.map +1 -0
  34. package/dist.node/models/hash.js +29 -0
  35. package/dist.node/models/hash.js.map +1 -0
  36. package/dist.node/models/index.js +47 -0
  37. package/dist.node/models/index.js.map +1 -0
  38. package/dist.node/models/license.js +103 -0
  39. package/dist.node/models/license.js.map +1 -0
  40. package/dist.node/models/metadata.js +35 -0
  41. package/dist.node/models/metadata.js.map +1 -0
  42. package/dist.node/models/organizationalContact.js +41 -0
  43. package/dist.node/models/organizationalContact.js.map +1 -0
  44. package/dist.node/models/organizationalEntity.js +31 -0
  45. package/dist.node/models/organizationalEntity.js.map +1 -0
  46. package/dist.node/models/swid.js +58 -0
  47. package/dist.node/models/swid.js.map +1 -0
  48. package/dist.node/models/tool.js +45 -0
  49. package/dist.node/models/tool.js.map +1 -0
  50. package/dist.node/resources.node.js +55 -0
  51. package/dist.node/resources.node.js.map +1 -0
  52. package/dist.node/serialize/_index.node.js +37 -0
  53. package/dist.node/serialize/_index.node.js.map +1 -0
  54. package/dist.node/serialize/baseSerializer.js +56 -0
  55. package/dist.node/serialize/baseSerializer.js.map +1 -0
  56. package/dist.node/serialize/bomRefDiscriminator.js +66 -0
  57. package/dist.node/serialize/bomRefDiscriminator.js.map +1 -0
  58. package/dist.node/serialize/index.js +55 -0
  59. package/dist.node/serialize/index.js.map +1 -0
  60. package/dist.node/serialize/json/index.js +47 -0
  61. package/dist.node/serialize/json/index.js.map +1 -0
  62. package/dist.node/serialize/json/normalize.js +431 -0
  63. package/dist.node/serialize/json/normalize.js.map +1 -0
  64. package/dist.node/serialize/json/types.js +35 -0
  65. package/dist.node/serialize/json/types.js.map +1 -0
  66. package/dist.node/serialize/jsonSerializer.js +55 -0
  67. package/dist.node/serialize/jsonSerializer.js.map +1 -0
  68. package/dist.node/serialize/types.js +21 -0
  69. package/dist.node/serialize/types.js.map +1 -0
  70. package/dist.node/serialize/xml/index.js +47 -0
  71. package/dist.node/serialize/xml/index.js.map +1 -0
  72. package/dist.node/serialize/xml/normalize.js +560 -0
  73. package/dist.node/serialize/xml/normalize.js.map +1 -0
  74. package/dist.node/serialize/xml/types.js +31 -0
  75. package/dist.node/serialize/xml/types.js.map +1 -0
  76. package/dist.node/serialize/xmlBaseSerializer.js +52 -0
  77. package/dist.node/serialize/xmlBaseSerializer.js.map +1 -0
  78. package/dist.node/serialize/xmlSerializer.node.js +30 -0
  79. package/dist.node/serialize/xmlSerializer.node.js.map +1 -0
  80. package/dist.node/spdx.js +35 -0
  81. package/dist.node/spdx.js.map +1 -0
  82. package/dist.node/spec.js +229 -0
  83. package/dist.node/spec.js.map +1 -0
  84. package/dist.node/types/cpe.js +28 -0
  85. package/dist.node/types/cpe.js.map +1 -0
  86. package/dist.node/types/index.js +39 -0
  87. package/dist.node/types/index.js.map +1 -0
  88. package/dist.node/types/integer.js +36 -0
  89. package/dist.node/types/integer.js.map +1 -0
  90. package/dist.node/types/mimeType.js +28 -0
  91. package/dist.node/types/mimeType.js.map +1 -0
  92. package/dist.node/types/urn.js +28 -0
  93. package/dist.node/types/urn.js.map +1 -0
  94. package/dist.web/lib.dev.js +3487 -0
  95. package/dist.web/lib.dev.js.map +1 -0
  96. package/dist.web/lib.js +2 -0
  97. package/dist.web/lib.js.LICENSE.txt +18 -0
  98. package/libs/universal-node-xml/index.d.ts +33 -0
  99. package/libs/universal-node-xml/index.js +42 -0
  100. package/libs/universal-node-xml/stringifiers/helpers.js +17 -0
  101. package/libs/universal-node-xml/stringifiers/xmlbuilder2.js +51 -0
  102. package/package.json +86 -0
  103. package/res/README.md +27 -0
  104. package/res/bom-1.0.SNAPSHOT.xsd +247 -0
  105. package/res/bom-1.1.SNAPSHOT.xsd +731 -0
  106. package/res/bom-1.2-strict.SNAPSHOT.schema.json +1026 -0
  107. package/res/bom-1.2.SNAPSHOT.schema.json +997 -0
  108. package/res/bom-1.2.SNAPSHOT.xsd +1418 -0
  109. package/res/bom-1.3-strict.SNAPSHOT.schema.json +1085 -0
  110. package/res/bom-1.3.SNAPSHOT.schema.json +1054 -0
  111. package/res/bom-1.3.SNAPSHOT.xsd +1631 -0
  112. package/res/bom-1.4.SNAPSHOT.schema.json +1697 -0
  113. package/res/bom-1.4.SNAPSHOT.xsd +2407 -0
  114. package/res/jsf-0.82.SNAPSHOT.schema.json +244 -0
  115. package/res/spdx.SNAPSHOT.schema.json +533 -0
  116. package/res/spdx.SNAPSHOT.xsd +2639 -0
  117. package/src/_index.node.ts +31 -0
  118. package/src/_index.web.ts +27 -0
  119. package/src/enums/attachmentEncoding.ts +22 -0
  120. package/src/enums/componentScope.ts +24 -0
  121. package/src/enums/componentType.ts +29 -0
  122. package/src/enums/externalReferenceType.ts +37 -0
  123. package/src/enums/hashAlogorithm.ts +33 -0
  124. package/src/enums/index.ts +24 -0
  125. package/src/factories/index.ts +20 -0
  126. package/src/factories/licenseFactory.ts +62 -0
  127. package/src/helpers/README.md +3 -0
  128. package/src/helpers/types.ts +28 -0
  129. package/src/models/attachment.ts +37 -0
  130. package/src/models/bom.ts +85 -0
  131. package/src/models/bomRef.ts +41 -0
  132. package/src/models/component.ts +136 -0
  133. package/src/models/externalReference.ts +48 -0
  134. package/src/models/hash.ts +38 -0
  135. package/src/models/index.ts +31 -0
  136. package/src/models/license.ts +133 -0
  137. package/src/models/metadata.ts +50 -0
  138. package/src/models/organizationalContact.ts +49 -0
  139. package/src/models/organizationalEntity.ts +38 -0
  140. package/src/models/swid.ts +71 -0
  141. package/src/models/tool.ts +58 -0
  142. package/src/resources.node.ts +59 -0
  143. package/src/serialize/_index.node.ts +23 -0
  144. package/src/serialize/_index.web.ts +23 -0
  145. package/src/serialize/baseSerializer.ts +52 -0
  146. package/src/serialize/bomRefDiscriminator.ts +69 -0
  147. package/src/serialize/index.ts +35 -0
  148. package/src/serialize/json/index.ts +23 -0
  149. package/src/serialize/json/normalize.ts +450 -0
  150. package/src/serialize/json/types.ts +187 -0
  151. package/src/serialize/jsonSerializer.ts +59 -0
  152. package/src/serialize/types.ts +38 -0
  153. package/src/serialize/xml/index.ts +23 -0
  154. package/src/serialize/xml/normalize.ts +590 -0
  155. package/src/serialize/xml/types.ts +112 -0
  156. package/src/serialize/xmlBaseSerializer.ts +52 -0
  157. package/src/serialize/xmlSerializer.node.ts +35 -0
  158. package/src/serialize/xmlSerializer.web.ts +89 -0
  159. package/src/spdx.ts +48 -0
  160. package/src/spec.ts +289 -0
  161. package/src/types/cpe.ts +33 -0
  162. package/src/types/index.ts +23 -0
  163. package/src/types/integer.ts +50 -0
  164. package/src/types/mimeType.ts +31 -0
  165. package/src/types/urn.ts +33 -0
  166. package/tsconfig.json +108 -0
  167. package/tsconfig.node.json +8 -0
  168. package/tsconfig.web.json +5 -0
  169. package/webpack.config.js +74 -0
@@ -0,0 +1,69 @@
1
+ /*!
2
+ This file is part of CycloneDX JavaScript Library.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+
16
+ SPDX-License-Identifier: Apache-2.0
17
+ Copyright (c) OWASP Foundation. All Rights Reserved.
18
+ */
19
+
20
+ import { BomRef } from '../models'
21
+
22
+ export class BomRefDiscriminator {
23
+ readonly #originalValues: ReadonlyMap<BomRef, string | undefined>
24
+
25
+ readonly #prefix: string
26
+
27
+ constructor (bomRefs: Iterable<BomRef>, prefix: string = 'BomRef') {
28
+ this.#originalValues = new Map(
29
+ Array.from(bomRefs).map(ref => [ref, ref.value])
30
+ )
31
+ this.#prefix = prefix
32
+ }
33
+
34
+ [Symbol.iterator] (): IterableIterator<BomRef> {
35
+ return this.#originalValues.keys()
36
+ }
37
+
38
+ discriminate (): void {
39
+ const knownRefValues = new Set<string>()
40
+ for (const [bomRef] of this.#originalValues) {
41
+ let value = bomRef.value
42
+ if (value === undefined || knownRefValues.has(value)) {
43
+ value = this.#makeUniqueId()
44
+ bomRef.value = value
45
+ }
46
+ knownRefValues.add(value)
47
+ }
48
+ }
49
+
50
+ reset (): void {
51
+ for (const [bomRef, originalValue] of this.#originalValues) {
52
+ bomRef.value = originalValue
53
+ }
54
+ }
55
+
56
+ /**
57
+ * generate a string in the format:
58
+ * <prefix>.<some-characters>.<some-characters>
59
+ */
60
+ #makeUniqueId (): string {
61
+ return `${
62
+ this.#prefix
63
+ }${
64
+ Math.random().toString(32).substring(1)
65
+ }${
66
+ Math.random().toString(32).substring(1)
67
+ }`
68
+ }
69
+ }
@@ -0,0 +1,35 @@
1
+ /*!
2
+ This file is part of CycloneDX JavaScript Library.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+
16
+ SPDX-License-Identifier: Apache-2.0
17
+ Copyright (c) OWASP Foundation. All Rights Reserved.
18
+ */
19
+
20
+ // not everything is public, yet
21
+
22
+ export * as Types from './types'
23
+
24
+ export * from './baseSerializer'
25
+ // export * from './baseDeserializer' // TODO
26
+
27
+ export * from './bomRefDiscriminator'
28
+
29
+ export * as JSON from './json'
30
+ export * from './jsonSerializer'
31
+ // export * from './jsonDeserializer' // TODO
32
+
33
+ export * as XML from './xml'
34
+ export * from './xmlBaseSerializer'
35
+ // export * from './xmlBaseDeserializer' // TODO
@@ -0,0 +1,23 @@
1
+ /*!
2
+ This file is part of CycloneDX JavaScript Library.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+
16
+ SPDX-License-Identifier: Apache-2.0
17
+ Copyright (c) OWASP Foundation. All Rights Reserved.
18
+ */
19
+
20
+ export * as Types from './types'
21
+
22
+ export * as Normalize from './normalize'
23
+ // export * as Denormalize from './denormalize' // TODO
@@ -0,0 +1,450 @@
1
+ /*!
2
+ This file is part of CycloneDX JavaScript Library.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+
16
+ SPDX-License-Identifier: Apache-2.0
17
+ Copyright (c) OWASP Foundation. All Rights Reserved.
18
+ */
19
+
20
+ import { isNotUndefined, Stringable } from '../../helpers/types'
21
+ import * as Models from '../../models'
22
+ import { Protocol as Spec, Version as SpecVersion } from '../../spec'
23
+ import { NormalizerOptions } from '../types'
24
+ import { JsonSchema, Normalized } from './types'
25
+
26
+ export class Factory {
27
+ readonly #spec: Spec
28
+
29
+ constructor (spec: Spec) {
30
+ this.#spec = spec
31
+ }
32
+
33
+ get spec (): Spec {
34
+ return this.#spec
35
+ }
36
+
37
+ makeForBom (): BomNormalizer {
38
+ return new BomNormalizer(this)
39
+ }
40
+
41
+ makeForMetadata (): MetadataNormalizer {
42
+ return new MetadataNormalizer(this)
43
+ }
44
+
45
+ makeForComponent (): ComponentNormalizer {
46
+ return new ComponentNormalizer(this)
47
+ }
48
+
49
+ makeForTool (): ToolNormalizer {
50
+ return new ToolNormalizer(this)
51
+ }
52
+
53
+ makeForOrganizationalContact (): OrganizationalContactNormalizer {
54
+ return new OrganizationalContactNormalizer(this)
55
+ }
56
+
57
+ makeForOrganizationalEntity (): OrganizationalEntityNormalizer {
58
+ return new OrganizationalEntityNormalizer(this)
59
+ }
60
+
61
+ makeForHash (): HashNormalizer {
62
+ return new HashNormalizer(this)
63
+ }
64
+
65
+ makeForLicense (): LicenseNormalizer {
66
+ return new LicenseNormalizer(this)
67
+ }
68
+
69
+ makeForSWID (): SWIDNormalizer {
70
+ return new SWIDNormalizer(this)
71
+ }
72
+
73
+ makeForExternalReference (): ExternalReferenceNormalizer {
74
+ return new ExternalReferenceNormalizer(this)
75
+ }
76
+
77
+ makeForAttachment (): AttachmentNormalizer {
78
+ return new AttachmentNormalizer(this)
79
+ }
80
+
81
+ makeForDependencyGraph (): DependencyGraphNormalizer {
82
+ return new DependencyGraphNormalizer(this)
83
+ }
84
+ }
85
+
86
+ const schemaUrl: ReadonlyMap<SpecVersion, string> = new Map([
87
+ [SpecVersion.v1dot2, 'http://cyclonedx.org/schema/bom-1.2b.schema.json'],
88
+ [SpecVersion.v1dot3, 'http://cyclonedx.org/schema/bom-1.3a.schema.json'],
89
+ [SpecVersion.v1dot4, 'http://cyclonedx.org/schema/bom-1.4.schema.json']
90
+ ])
91
+
92
+ interface Normalizer {
93
+ normalize: (data: object, options: NormalizerOptions) => object | undefined
94
+
95
+ normalizeIter?: (data: Iterable<object>, options: NormalizerOptions) => object[]
96
+ }
97
+
98
+ abstract class Base implements Normalizer {
99
+ protected readonly _factory: Factory
100
+
101
+ constructor (factory: Factory) {
102
+ this._factory = factory
103
+ }
104
+
105
+ abstract normalize (data: object, options: NormalizerOptions): object | undefined
106
+ }
107
+
108
+ /* eslint-disable @typescript-eslint/prefer-nullish-coalescing, @typescript-eslint/strict-boolean-expressions --
109
+ * since empty strings need to be treated as undefined/null
110
+ */
111
+
112
+ export class BomNormalizer extends Base {
113
+ normalize (data: Models.Bom, options: NormalizerOptions): Normalized.Bom {
114
+ return {
115
+ $schema: schemaUrl.get(this._factory.spec.version),
116
+ bomFormat: 'CycloneDX',
117
+ specVersion: this._factory.spec.version,
118
+ version: data.version,
119
+ serialNumber: data.serialNumber,
120
+ metadata: this._factory.makeForMetadata().normalize(data.metadata, options),
121
+ components: data.components.size > 0
122
+ ? this._factory.makeForComponent().normalizeIter(data.components, options)
123
+ // spec < 1.4 requires `component` to be array
124
+ : [],
125
+ dependencies: this._factory.spec.supportsDependencyGraph
126
+ ? this._factory.makeForDependencyGraph().normalize(data, options)
127
+ : undefined
128
+ }
129
+ }
130
+ }
131
+
132
+ export class MetadataNormalizer extends Base {
133
+ normalize (data: Models.Metadata, options: NormalizerOptions): Normalized.Metadata {
134
+ const orgEntityNormalizer = this._factory.makeForOrganizationalEntity()
135
+ return {
136
+ timestamp: data.timestamp?.toISOString(),
137
+ tools: data.tools.size > 0
138
+ ? this._factory.makeForTool().normalizeIter(data.tools, options)
139
+ : undefined,
140
+ authors: data.authors.size > 0
141
+ ? this._factory.makeForOrganizationalContact().normalizeIter(data.authors, options)
142
+ : undefined,
143
+ component: data.component === undefined
144
+ ? undefined
145
+ : this._factory.makeForComponent().normalize(data.component, options),
146
+ manufacture: data.manufacture === undefined
147
+ ? undefined
148
+ : orgEntityNormalizer.normalize(data.manufacture, options),
149
+ supplier: data.supplier === undefined
150
+ ? undefined
151
+ : orgEntityNormalizer.normalize(data.supplier, options)
152
+ }
153
+ }
154
+ }
155
+
156
+ export class ToolNormalizer extends Base {
157
+ normalize (data: Models.Tool, options: NormalizerOptions): Normalized.Tool {
158
+ return {
159
+ vendor: data.vendor || undefined,
160
+ name: data.name || undefined,
161
+ version: data.version || undefined,
162
+ hashes: data.hashes.size > 0
163
+ ? this._factory.makeForHash().normalizeIter(data.hashes, options)
164
+ : undefined,
165
+ externalReferences: this._factory.spec.supportsToolReferences && data.externalReferences.size > 0
166
+ ? this._factory.makeForExternalReference().normalizeIter(data.externalReferences, options)
167
+ : undefined
168
+ }
169
+ }
170
+
171
+ normalizeIter (data: Iterable<Models.Tool>, options: NormalizerOptions): Normalized.Tool[] {
172
+ const tools = Array.from(data)
173
+ if (options.sortLists ?? false) {
174
+ tools.sort(Models.ToolRepository.compareItems)
175
+ }
176
+ return tools.map(t => this.normalize(t, options))
177
+ }
178
+ }
179
+
180
+ export class HashNormalizer extends Base {
181
+ normalize ([algorithm, content]: Models.Hash, options: NormalizerOptions): Normalized.Hash | undefined {
182
+ const spec = this._factory.spec
183
+ return spec.supportsHashAlgorithm(algorithm) && spec.supportsHashValue(content)
184
+ ? {
185
+ alg: algorithm,
186
+ content: content
187
+ }
188
+ : undefined
189
+ }
190
+
191
+ normalizeIter (data: Iterable<Models.Hash>, options: NormalizerOptions): Normalized.Hash[] {
192
+ const hashes = Array.from(data)
193
+ if (options.sortLists ?? false) {
194
+ hashes.sort(Models.HashRepository.compareItems)
195
+ }
196
+ return hashes.map(h => this.normalize(h, options))
197
+ .filter(isNotUndefined)
198
+ }
199
+ }
200
+
201
+ export class OrganizationalContactNormalizer extends Base {
202
+ normalize (data: Models.OrganizationalContact, options: NormalizerOptions): Normalized.OrganizationalContact {
203
+ return {
204
+ name: data.name || undefined,
205
+ email: JsonSchema.isIdnEmail(data.email)
206
+ ? data.email
207
+ : undefined,
208
+ phone: data.phone || undefined
209
+ }
210
+ }
211
+
212
+ normalizeIter (data: Iterable<Models.OrganizationalContact>, options: NormalizerOptions): Normalized.OrganizationalContact[] {
213
+ const contacts = Array.from(data)
214
+ if (options.sortLists ?? false) {
215
+ contacts.sort(Models.OrganizationalContactRepository.compareItems)
216
+ }
217
+ return contacts.map(c => this.normalize(c, options))
218
+ }
219
+ }
220
+
221
+ export class OrganizationalEntityNormalizer extends Base {
222
+ normalize (data: Models.OrganizationalEntity, options: NormalizerOptions): Normalized.OrganizationalEntity {
223
+ const urls = normalizeStringableIter(data.url, options)
224
+ .filter(JsonSchema.isIriReference)
225
+ return {
226
+ name: data.name || undefined,
227
+ /** must comply to {@link https://datatracker.ietf.org/doc/html/rfc3987} */
228
+ url: urls.length > 0
229
+ ? urls
230
+ : undefined,
231
+ contact: data.contact.size > 0
232
+ ? this._factory.makeForOrganizationalContact().normalizeIter(data.contact, options)
233
+ : undefined
234
+ }
235
+ }
236
+ }
237
+
238
+ export class ComponentNormalizer extends Base {
239
+ normalize (data: Models.Component, options: NormalizerOptions): Normalized.Component | undefined {
240
+ return this._factory.spec.supportsComponentType(data.type)
241
+ ? {
242
+ type: data.type,
243
+ name: data.name,
244
+ group: data.group || undefined,
245
+ // version fallback to string for spec < 1.4
246
+ version: data.version || '',
247
+ 'bom-ref': data.bomRef.value || undefined,
248
+ supplier: data.supplier === undefined
249
+ ? undefined
250
+ : this._factory.makeForOrganizationalEntity().normalize(data.supplier, options),
251
+ author: data.author || undefined,
252
+ publisher: data.publisher || undefined,
253
+ description: data.description || undefined,
254
+ scope: data.scope,
255
+ hashes: data.hashes.size > 0
256
+ ? this._factory.makeForHash().normalizeIter(data.hashes, options)
257
+ : undefined,
258
+ licenses: data.licenses.size > 0
259
+ ? this._factory.makeForLicense().normalizeIter(data.licenses, options)
260
+ : undefined,
261
+ copyright: data.copyright || undefined,
262
+ cpe: data.cpe || undefined,
263
+ purl: data.purl?.toString(),
264
+ swid: data.swid === undefined
265
+ ? undefined
266
+ : this._factory.makeForSWID().normalize(data.swid, options),
267
+ externalReferences: data.externalReferences.size > 0
268
+ ? this._factory.makeForExternalReference().normalizeIter(data.externalReferences, options)
269
+ : undefined
270
+ }
271
+ : undefined
272
+ }
273
+
274
+ normalizeIter (data: Iterable<Models.Component>, options: NormalizerOptions): Normalized.Component[] {
275
+ const components = Array.from(data)
276
+ if (options.sortLists ?? false) {
277
+ components.sort(Models.ComponentRepository.compareItems)
278
+ }
279
+ return components.map(c => this.normalize(c, options))
280
+ .filter(isNotUndefined)
281
+ }
282
+ }
283
+
284
+ export class LicenseNormalizer extends Base {
285
+ normalize (data: Models.License, options: NormalizerOptions): Normalized.License {
286
+ switch (true) {
287
+ case data instanceof Models.NamedLicense:
288
+ return this.#normalizeNamedLicense(data as Models.NamedLicense, options)
289
+ case data instanceof Models.SpdxLicense:
290
+ return this.#normalizeSpdxLicense(data as Models.SpdxLicense, options)
291
+ case data instanceof Models.LicenseExpression:
292
+ return this.#normalizeLicenseExpression(data as Models.LicenseExpression)
293
+ default:
294
+ // this case is not expected to happen - and therefore is undocumented
295
+ throw new TypeError('Unexpected LicenseChoice')
296
+ }
297
+ }
298
+
299
+ #normalizeNamedLicense (data: Models.NamedLicense, options: NormalizerOptions): Normalized.NamedLicense {
300
+ return {
301
+ license: {
302
+ name: data.name,
303
+ text: data.text === undefined
304
+ ? undefined
305
+ : this._factory.makeForAttachment().normalize(data.text, options),
306
+ url: data.url?.toString()
307
+ }
308
+ }
309
+ }
310
+
311
+ #normalizeSpdxLicense (data: Models.SpdxLicense, options: NormalizerOptions): Normalized.SpdxLicense {
312
+ return {
313
+ license: {
314
+ id: data.id,
315
+ text: data.text === undefined
316
+ ? undefined
317
+ : this._factory.makeForAttachment().normalize(data.text, options),
318
+ url: data.url?.toString()
319
+ }
320
+ }
321
+ }
322
+
323
+ #normalizeLicenseExpression (data: Models.LicenseExpression): Normalized.LicenseExpression {
324
+ return {
325
+ expression: data.expression
326
+ }
327
+ }
328
+
329
+ normalizeIter (data: Iterable<Models.License>, options: NormalizerOptions): Normalized.License[] {
330
+ const licenses = Array.from(data)
331
+ if (options.sortLists ?? false) {
332
+ licenses.sort(Models.LicenseRepository.compareItems)
333
+ }
334
+ return licenses.map(c => this.normalize(c, options))
335
+ }
336
+ }
337
+
338
+ export class SWIDNormalizer extends Base {
339
+ normalize (data: Models.SWID, options: NormalizerOptions): Normalized.SWID {
340
+ const url = data.url?.toString()
341
+ return {
342
+ tagId: data.tagId,
343
+ name: data.name,
344
+ version: data.version || undefined,
345
+ tagVersion: data.tagVersion,
346
+ patch: data.patch,
347
+ text: data.text === undefined
348
+ ? undefined
349
+ : this._factory.makeForAttachment().normalize(data.text, options),
350
+ url: JsonSchema.isIriReference(url)
351
+ ? url
352
+ : undefined
353
+ }
354
+ }
355
+ }
356
+
357
+ export class ExternalReferenceNormalizer extends Base {
358
+ normalize (data: Models.ExternalReference, options: NormalizerOptions): Normalized.ExternalReference | undefined {
359
+ return this._factory.spec.supportsExternalReferenceType(data.type)
360
+ ? {
361
+ url: data.url.toString(),
362
+ type: data.type,
363
+ comment: data.comment || undefined
364
+ }
365
+ : undefined
366
+ }
367
+
368
+ normalizeIter (data: Iterable<Models.ExternalReference>, options: NormalizerOptions): Normalized.ExternalReference[] {
369
+ const refs = Array.from(data)
370
+ if (options.sortLists ?? false) {
371
+ refs.sort(Models.ExternalReferenceRepository.compareItems)
372
+ }
373
+ return refs.map(r => this.normalize(r, options))
374
+ .filter(isNotUndefined)
375
+ }
376
+ }
377
+
378
+ export class AttachmentNormalizer extends Base {
379
+ normalize (data: Models.Attachment, options: NormalizerOptions): Normalized.Attachment {
380
+ return {
381
+ content: data.content,
382
+ contentType: data.contentType || undefined,
383
+ encoding: data.encoding
384
+ }
385
+ }
386
+ }
387
+
388
+ export class DependencyGraphNormalizer extends Base {
389
+ normalize (data: Models.Bom, options: NormalizerOptions): Normalized.Dependency[] | undefined {
390
+ if (!data.metadata.component?.bomRef.value) {
391
+ // the graph is missing the entry point -> omit the graph
392
+ return undefined
393
+ }
394
+
395
+ const allRefs = new Map<Models.BomRef, Models.BomRefRepository>()
396
+ for (const c of data.components) {
397
+ allRefs.set(c.bomRef, new Models.BomRefRepository(c.dependencies))
398
+ }
399
+ allRefs.set(data.metadata.component.bomRef, data.metadata.component.dependencies)
400
+
401
+ const normalized: Normalized.Dependency[] = []
402
+ for (const [ref, deps] of allRefs) {
403
+ const dep = this.#normalizeDependency(ref, deps, allRefs, options)
404
+ if (isNotUndefined(dep)) {
405
+ normalized.push(dep)
406
+ }
407
+ }
408
+
409
+ if (options.sortLists ?? false) {
410
+ normalized.sort(({ ref: a }, { ref: b }) => a.localeCompare(b))
411
+ }
412
+
413
+ return normalized
414
+ }
415
+
416
+ #normalizeDependency (
417
+ ref: Models.BomRef,
418
+ deps: Models.BomRefRepository,
419
+ allRefs: Map<Models.BomRef, Models.BomRefRepository>,
420
+ options: NormalizerOptions
421
+ ): Normalized.Dependency | undefined {
422
+ const bomRef = ref.toString()
423
+ if (bomRef.length === 0) {
424
+ // no value -> cannot render
425
+ return undefined
426
+ }
427
+
428
+ const dependsOn: string[] = normalizeStringableIter(
429
+ Array.from(deps).filter(d => allRefs.has(d) && d !== ref),
430
+ options
431
+ ).filter(d => d.length > 0)
432
+
433
+ return {
434
+ ref: bomRef,
435
+ dependsOn: dependsOn.length > 0
436
+ ? dependsOn
437
+ : undefined
438
+ }
439
+ }
440
+ }
441
+
442
+ /* eslint-enable @typescript-eslint/prefer-nullish-coalescing, @typescript-eslint/strict-boolean-expressions */
443
+
444
+ function normalizeStringableIter (data: Iterable<Stringable>, options: NormalizerOptions): string[] {
445
+ const r: string[] = Array.from(data, d => d.toString())
446
+ if (options.sortLists ?? false) {
447
+ r.sort((a, b) => a.localeCompare(b))
448
+ }
449
+ return r
450
+ }