| 22 | import { PackageURL } from './package-url.js' |
| 23 | import { |
| 24 | ArrayPrototypeMap, |
| 25 | ObjectEntries, |
| 26 | ObjectFromEntries, |
| 27 | } from '@socketsecurity/lib/primordials' |
| 28 | |
| 29 | import type { QualifiersObject } from './purl-component.js' |
| 57 | export class PurlBuilder { |
| 76 | private _type?: string | undefined |
| 79 | private _namespace?: string | undefined |
| 82 | private _name?: string | undefined |
| 85 | private _version?: string | undefined |
| 88 | private _qualifiers?: Record<string, string> | undefined |
| 91 | private _subpath?: string | undefined |
| 102 | build(): PackageURL { |
| 103 | return new PackageURL( |
| 104 | this._type, |
| 105 | this._namespace, |
| 106 | this._name, |
| 107 | this._version, |
| 108 | this._qualifiers, |
| 109 | this._subpath, |
| 110 | ) |
| 111 | } |
| 120 | name(name: string): this { |
| 121 | this._name = name |
| 122 | return this |
| 123 | } |
| 133 | namespace(namespace: string): this { |
| 134 | this._namespace = namespace |
| 135 | return this |
| 136 | } |
| 144 | qualifier(key: string, value: string): this { |
| 145 | if (!this._qualifiers) { |
| 146 | this._qualifiers = { __proto__: null } as unknown as Record< |
| 147 | string, |
| 148 | string |
| 149 | > |
| 150 | } |
| 151 | this._qualifiers[key] = value |
| 152 | return this |
| 153 | } |
| 163 | qualifiers(qualifiers: Record<string, string>): this { |
| 164 | this._qualifiers = { __proto__: null, ...qualifiers } as unknown as Record< |
| 165 | string, |
| 166 | string |
| 167 | > |
| 168 | return this |
| 169 | } |
| 178 | subpath(subpath: string): this { |
| 179 | this._subpath = subpath |
| 180 | return this |
| 181 | } |
| 186 | type(type: string): this { |
| 187 | this._type = type |
| 188 | return this |
| 189 | } |
| 197 | version(version: string): this { |
| 198 | this._version = version |
| 199 | return this |
| 200 | } |
| 207 | static bitbucket(): PurlBuilder { |
| 208 | return new PurlBuilder().type('bitbucket') |
| 209 | } |
| 216 | static cargo(): PurlBuilder { |
| 217 | return new PurlBuilder().type('cargo') |
| 218 | } |
| 225 | static cocoapods(): PurlBuilder { |
| 226 | return new PurlBuilder().type('cocoapods') |
| 227 | } |
| 234 | static composer(): PurlBuilder { |
| 235 | return new PurlBuilder().type('composer') |
| 236 | } |
| 243 | static conan(): PurlBuilder { |
| 244 | return new PurlBuilder().type('conan') |
| 245 | } |
| 252 | static conda(): PurlBuilder { |
| 253 | return new PurlBuilder().type('conda') |
| 254 | } |
| 261 | static cran(): PurlBuilder { |
| 262 | return new PurlBuilder().type('cran') |
| 263 | } |
| 271 | static create(): PurlBuilder { |
| 272 | return new PurlBuilder() |
| 273 | } |
| 280 | static deb(): PurlBuilder { |
| 281 | return new PurlBuilder().type('deb') |
| 282 | } |
| 289 | static docker(): PurlBuilder { |
| 290 | return new PurlBuilder().type('docker') |
| 291 | } |
| 299 | static from(purl: PackageURL): PurlBuilder { |
| 300 | const builder = new PurlBuilder() |
| 301 | if (purl.type !== undefined) { |
| 302 | builder._type = purl.type |
| 303 | } |
| 304 | if (purl.namespace !== undefined) { |
| 305 | builder._namespace = purl.namespace |
| 306 | } |
| 307 | if (purl.name !== undefined) { |
| 308 | builder._name = purl.name |
| 309 | } |
| 310 | if (purl.version !== undefined) { |
| 311 | builder._version = purl.version |
| 312 | } |
| 313 | if (purl.qualifiers !== undefined) { |
| 314 | const qualifiersObj = purl.qualifiers as QualifiersObject |
| 315 | builder._qualifiers = ObjectFromEntries( |
| 316 | ArrayPrototypeMap(ObjectEntries(qualifiersObj), ([key, value]) => [ |
| 317 | key, |
| 318 | String(value), |
| 319 | ]), |
| 320 | ) as Record<string, string> |
| 321 | } |
| 322 | if (purl.subpath !== undefined) { |
| 323 | builder._subpath = purl.subpath |
| 324 | } |
| 325 | return builder |
| 326 | } |
| 333 | static gem(): PurlBuilder { |
| 334 | return new PurlBuilder().type('gem') |
| 335 | } |
| 342 | static github(): PurlBuilder { |
| 343 | return new PurlBuilder().type('github') |
| 344 | } |
| 351 | static gitlab(): PurlBuilder { |
| 352 | return new PurlBuilder().type('gitlab') |
| 353 | } |
| 360 | static golang(): PurlBuilder { |
| 361 | return new PurlBuilder().type('golang') |
| 362 | } |
| 369 | static hackage(): PurlBuilder { |
| 370 | return new PurlBuilder().type('hackage') |
| 371 | } |
| 378 | static hex(): PurlBuilder { |
| 379 | return new PurlBuilder().type('hex') |
| 380 | } |
| 387 | static huggingface(): PurlBuilder { |
| 388 | return new PurlBuilder().type('huggingface') |
| 389 | } |
| 396 | static luarocks(): PurlBuilder { |
| 397 | return new PurlBuilder().type('luarocks') |
| 398 | } |
| 405 | static maven(): PurlBuilder { |
| 406 | return new PurlBuilder().type('maven') |
| 407 | } |
| 414 | static npm(): PurlBuilder { |
| 415 | return new PurlBuilder().type('npm') |
| 416 | } |
| 423 | static nuget(): PurlBuilder { |
| 424 | return new PurlBuilder().type('nuget') |
| 425 | } |
| 432 | static oci(): PurlBuilder { |
| 433 | return new PurlBuilder().type('oci') |
| 434 | } |
| 441 | static pub(): PurlBuilder { |
| 442 | return new PurlBuilder().type('pub') |
| 443 | } |
| 450 | static pypi(): PurlBuilder { |
| 451 | return new PurlBuilder().type('pypi') |
| 452 | } |
| 459 | static rpm(): PurlBuilder { |
| 460 | return new PurlBuilder().type('rpm') |
| 461 | } |
| 468 | static swift(): PurlBuilder { |
| 469 | return new PurlBuilder().type('swift') |
| 470 | } |
| 471 | } |
| 5 | import { |
| 6 | encodeComponent, |
| 7 | encodeName, |
| 8 | encodeNamespace, |
| 9 | encodeQualifiers, |
| 10 | encodeSubpath, |
| 11 | encodeVersion, |
| 12 | } from './encode.js' |
| 13 | import { isNonEmptyString } from './strings.js' |
| 14 | |
| 15 | import type { PackageURL } from './package-url.js' |
| 16 | import type { QualifiersObject } from './purl-component.js' |
| 36 | export function stringifySpec(purl: PackageURL): string { |
| 37 | const { |
| 38 | name, |
| 39 | namespace, |
| 40 | qualifiers, |
| 41 | subpath, |
| 42 | version, |
| 43 | }: { |
| 44 | name?: string | undefined |
| 45 | namespace?: string | undefined |
| 46 | qualifiers?: QualifiersObject | undefined |
| 47 | subpath?: string | undefined |
| 48 | version?: string | undefined |
| 49 | } = purl |
| 50 | let specStr = '' |
| 51 | if (isNonEmptyString(namespace)) { |
| 52 | specStr = `${encodeNamespace(namespace)}/` |
| 53 | } |
| 54 | specStr = `${specStr}${encodeName(name)}` |
| 55 | if (isNonEmptyString(version)) { |
| 56 | specStr = `${specStr}@${encodeVersion(version)}` |
| 57 | } |
| 58 | if (qualifiers) { |
| 59 | specStr = `${specStr}?${encodeQualifiers(qualifiers)}` |
| 60 | } |
| 61 | if (isNonEmptyString(subpath)) { |
| 62 | specStr = `${specStr}#${encodeSubpath(subpath)}` |
| 63 | } |
| 64 | return specStr |
| 65 | } |
| 83 | export function stringify(purl: PackageURL): string { |
| 84 | const type = isNonEmptyString(purl.type) ? encodeComponent(purl.type) : '' |
| 85 | return `pkg:${type}/${stringifySpec(purl)}` |
| 86 | } |