metascope 0.8.4 → 0.9.0

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 (103) hide show
  1. package/dist/bin/cli.js +30426 -29237
  2. package/dist/bin/{jiti-DDHyYNLW.js → jiti-B26uDQuv.js} +2 -2
  3. package/dist/bin/renovate-CEC0BhMB.js +1 -0
  4. package/dist/bin/shared-Ch5JsD6s.js +8 -0
  5. package/dist/bin/shared2-Dhjk_Okb.js +1 -0
  6. package/dist/bin/web-tree-sitter.wasm +0 -0
  7. package/dist/lib/data/license-urls.js +2904 -0
  8. package/dist/lib/file-matching.js +5 -2
  9. package/dist/lib/log.d.ts +0 -1
  10. package/dist/lib/metadata-types.d.ts +16 -9
  11. package/dist/lib/metadata.d.ts +0 -1
  12. package/dist/lib/metadata.js +5 -5
  13. package/dist/lib/package.js +1 -1
  14. package/dist/lib/parsers/configparser-parser.js +14 -12
  15. package/dist/lib/parsers/gemspec-parser.js +41 -42
  16. package/dist/lib/parsers/go-mod-parser.js +51 -45
  17. package/dist/lib/parsers/makefile-config-parser.js +7 -6
  18. package/dist/lib/parsers/properties-parser.js +1 -1
  19. package/dist/lib/parsers/rfc822-header-parser.js +8 -6
  20. package/dist/lib/parsers/setup-py-parser.js +28 -24
  21. package/dist/lib/source.d.ts +6 -2
  22. package/dist/lib/sources/arduino-library-properties.d.ts +0 -1
  23. package/dist/lib/sources/arduino-library-properties.js +6 -7
  24. package/dist/lib/sources/cinder-cinderblock-xml.d.ts +0 -1
  25. package/dist/lib/sources/cinder-cinderblock-xml.js +5 -5
  26. package/dist/lib/sources/code-stats.d.ts +3 -2
  27. package/dist/lib/sources/codemeta-json.d.ts +0 -1
  28. package/dist/lib/sources/codemeta-json.js +21 -20
  29. package/dist/lib/sources/dependency-updates.d.ts +17 -8
  30. package/dist/lib/sources/dependency-updates.js +38 -34
  31. package/dist/lib/sources/file-stats.d.ts +7 -4
  32. package/dist/lib/sources/git-config.d.ts +0 -1
  33. package/dist/lib/sources/git-stats.d.ts +47 -24
  34. package/dist/lib/sources/git-stats.js +51 -33
  35. package/dist/lib/sources/github-actions.d.ts +0 -1
  36. package/dist/lib/sources/github-actions.js +4 -3
  37. package/dist/lib/sources/github.d.ts +156 -78
  38. package/dist/lib/sources/github.js +11 -8
  39. package/dist/lib/sources/go-go-mod.d.ts +0 -1
  40. package/dist/lib/sources/go-goreleaser-yaml.d.ts +0 -1
  41. package/dist/lib/sources/go-goreleaser-yaml.js +5 -3
  42. package/dist/lib/sources/java-pom-xml.d.ts +0 -1
  43. package/dist/lib/sources/java-pom-xml.js +12 -12
  44. package/dist/lib/sources/license-file.d.ts +0 -1
  45. package/dist/lib/sources/metadata-file.d.ts +0 -1
  46. package/dist/lib/sources/metadata-file.js +9 -10
  47. package/dist/lib/sources/metascope.d.ts +9 -5
  48. package/dist/lib/sources/node-npm-registry.d.ts +21 -11
  49. package/dist/lib/sources/node-npm-registry.js +12 -6
  50. package/dist/lib/sources/node-package-json.d.ts +0 -1
  51. package/dist/lib/sources/obsidian-plugin-manifest-json.d.ts +17 -9
  52. package/dist/lib/sources/obsidian-plugin-manifest-json.js +2 -2
  53. package/dist/lib/sources/obsidian-plugin-registry.d.ts +3 -2
  54. package/dist/lib/sources/obsidian-plugin-registry.js +2 -1
  55. package/dist/lib/sources/openframeworks-addon-config-mk.d.ts +0 -1
  56. package/dist/lib/sources/openframeworks-install-xml.d.ts +0 -1
  57. package/dist/lib/sources/openframeworks-install-xml.js +3 -3
  58. package/dist/lib/sources/processing-library-properties.d.ts +0 -1
  59. package/dist/lib/sources/processing-library-properties.js +9 -10
  60. package/dist/lib/sources/processing-sketch-properties.d.ts +0 -1
  61. package/dist/lib/sources/processing-sketch-properties.js +13 -20
  62. package/dist/lib/sources/publiccode-yaml.d.ts +0 -1
  63. package/dist/lib/sources/publiccode-yaml.js +61 -58
  64. package/dist/lib/sources/python-pkg-info.d.ts +0 -1
  65. package/dist/lib/sources/python-pkg-info.js +6 -5
  66. package/dist/lib/sources/python-pypi-registry.d.ts +21 -11
  67. package/dist/lib/sources/python-pypi-registry.js +23 -16
  68. package/dist/lib/sources/python-pyproject-toml.d.ts +0 -1
  69. package/dist/lib/sources/python-setup-cfg.d.ts +2 -3
  70. package/dist/lib/sources/python-setup-cfg.js +10 -9
  71. package/dist/lib/sources/python-setup-py.d.ts +0 -1
  72. package/dist/lib/sources/readme-file.d.ts +0 -1
  73. package/dist/lib/sources/readme-file.js +4 -3
  74. package/dist/lib/sources/ruby-gemspec.d.ts +0 -1
  75. package/dist/lib/sources/rust-cargo-toml.d.ts +0 -1
  76. package/dist/lib/sources/rust-cargo-toml.js +1 -1
  77. package/dist/lib/sources/xcode-info-plist.d.ts +0 -1
  78. package/dist/lib/sources/xcode-info-plist.js +29 -31
  79. package/dist/lib/sources/xcode-project-pbxproj.d.ts +0 -1
  80. package/dist/lib/sources/xcode-project-pbxproj.js +17 -16
  81. package/dist/lib/templates/codemeta-json.d.ts +1 -0
  82. package/dist/lib/templates/codemeta.d.ts +1 -0
  83. package/dist/lib/templates/codemeta.js +98 -89
  84. package/dist/lib/templates/frontmatter.d.ts +2 -1
  85. package/dist/lib/templates/frontmatter.js +1 -1
  86. package/dist/lib/templates/index.d.ts +2 -1
  87. package/dist/lib/templates/index.js +1 -1
  88. package/dist/lib/templates/metadata.d.ts +1 -0
  89. package/dist/lib/templates/project.d.ts +1 -0
  90. package/dist/lib/utilities/codemeta-helpers.js +14 -13
  91. package/dist/lib/utilities/fetch.js +18 -2
  92. package/dist/lib/utilities/formatting.js +1 -1
  93. package/dist/lib/utilities/github.js +2 -2
  94. package/dist/lib/utilities/license-identification.d.ts +9 -4
  95. package/dist/lib/utilities/license-identification.js +37 -23
  96. package/dist/lib/utilities/template-helpers.d.ts +7 -5
  97. package/dist/lib/utilities/template-helpers.js +15 -14
  98. package/dist/lib/utilities/tree-sitter-wasm.d.ts +0 -1
  99. package/dist/lib/utilities/tree-sitter-wasm.js +1 -1
  100. package/package.json +18 -17
  101. package/readme.md +13 -11
  102. package/dist/bin/renovate-CpDMbQZd.js +0 -1
  103. package/dist/bin/shared-Dix5HPlG.js +0 -6
@@ -27,10 +27,10 @@ import is from "@sindresorhus/is";
27
27
  * Set `INFER_TARGET_PRODUCT` to `true` to enable heuristic inference of
28
28
  * `targetProduct` from available signals (bin field, browser field, etc.).
29
29
  */
30
- const PEP508_NAME_REGEX = /^[\w.-]+/;
31
- const DATE_ONLY_REGEX = /^\d{4}-\d{2}-\d{2}$/;
32
- const DATETIME_DATE_REGEX = /^(\d{4}-\d{2}-\d{2})T/;
33
- const TRAILING_DOT_GIT_REGEX = /\.git$/;
30
+ const PEP508_NAME_REGEX = /^[\w.\-]+/v;
31
+ const DATE_ONLY_REGEX = /^\d{4}-\d{2}-\d{2}$/v;
32
+ const DATETIME_DATE_REGEX = /^(\d{4}-\d{2}-\d{2})T/v;
33
+ const TRAILING_DOT_GIT_REGEX = /\.git$/v;
34
34
  const codemeta = defineTemplate(({ arduinoLibraryProperties, cinderCinderblockXml, codemetaJson: codemetaRaw, codeStats, fileStats, github: githubRaw, gitStats: gitRaw, goGoMod, javaPomXml, licenseFile, metascope, nodeNpmRegistry: npmRaw, nodePackageJson, obsidianPluginManifestJson, openframeworksAddonConfigMk, openframeworksInstallXml, processingLibraryProperties, publiccodeYaml, pythonPkgInfo, pythonPypiRegistry: pypiRaw, pythonPyprojectToml, pythonSetupCfg, pythonSetupPy, readmeFile, rubyGemspec, rustCargoToml, xcodeInfoPlist }) => {
35
35
  const cm = firstOf(codemetaRaw);
36
36
  const github = firstOf(githubRaw);
@@ -40,7 +40,7 @@ const codemeta = defineTemplate(({ arduinoLibraryProperties, cinderCinderblockXm
40
40
  const package_ = firstOf(nodePackageJson);
41
41
  const pyproject = firstOf(pythonPyprojectToml);
42
42
  const setupPy = firstOf(pythonSetupPy);
43
- const setupCfg = firstOf(pythonSetupCfg);
43
+ const setupConfig = firstOf(pythonSetupCfg);
44
44
  const pkgInfo = firstOf(pythonPkgInfo);
45
45
  const cargo = firstOf(rustCargoToml);
46
46
  const gem = firstOf(rubyGemspec);
@@ -58,9 +58,9 @@ const codemeta = defineTemplate(({ arduinoLibraryProperties, cinderCinderblockXm
58
58
  const readmeFileFirst = firstOf(readmeFile);
59
59
  const fileStatsFirst = firstOf(fileStats);
60
60
  const poetry = pyproject?.data.tool?.poetry;
61
- const name = package_?.data.name ?? pyproject?.data.project?.name ?? poetry?.name ?? setupPy?.data.name ?? setupCfg?.data.name ?? pkgInfo?.data.name ?? cargo?.data.name ?? gem?.data.name ?? pom?.data.name ?? goMod?.data.module ?? arduino?.data.name ?? processing?.data.name ?? ofAddon?.data.name ?? ofInstall?.data.name ?? cinder?.data.name ?? xcode?.data.name ?? obsidian?.data.name ?? publiccode?.data.name ?? cm?.data.name ?? readmeFileFirst?.data.name ?? fileStatsFirst?.data.folderName;
62
- const description = package_?.data.description ?? pyproject?.data.project?.description ?? poetry?.description ?? setupPy?.data.description ?? setupCfg?.data.description ?? pkgInfo?.data.summary ?? cargo?.data.description ?? gem?.data.summary ?? pom?.data.description ?? arduino?.data.sentence ?? processing?.data.sentence ?? ofAddon?.data.description ?? ofInstall?.data.description ?? cinder?.data.summary ?? xcode?.data.description ?? obsidian?.data.description ?? publiccode?.data.description?.shortDescription ?? cm?.data.description ?? github?.data.description;
63
- const version = package_?.data.version ?? pyproject?.data.project?.version ?? poetry?.version ?? setupPy?.data.version ?? setupCfg?.data.version ?? pkgInfo?.data.version ?? cargo?.data.version ?? gem?.data.version ?? pom?.data.version ?? arduino?.data.version ?? processing?.data.prettyVersion ?? ofInstall?.data.version ?? cinder?.data.version ?? xcode?.data.version ?? obsidian?.data.version ?? publiccode?.data.softwareVersion ?? cm?.data.version ?? cm?.data.softwareVersion;
61
+ const name = package_?.data.name ?? pyproject?.data.project?.name ?? poetry?.name ?? setupPy?.data.name ?? setupConfig?.data.name ?? pkgInfo?.data.name ?? cargo?.data.name ?? gem?.data.name ?? pom?.data.name ?? goMod?.data.module ?? arduino?.data.name ?? processing?.data.name ?? ofAddon?.data.name ?? ofInstall?.data.name ?? cinder?.data.name ?? xcode?.data.name ?? obsidian?.data.name ?? publiccode?.data.name ?? cm?.data.name ?? readmeFileFirst?.data.name ?? fileStatsFirst?.data.folderName;
62
+ const description = package_?.data.description ?? pyproject?.data.project?.description ?? poetry?.description ?? setupPy?.data.description ?? setupConfig?.data.description ?? pkgInfo?.data.summary ?? cargo?.data.description ?? gem?.data.summary ?? pom?.data.description ?? arduino?.data.sentence ?? processing?.data.sentence ?? ofAddon?.data.description ?? ofInstall?.data.description ?? cinder?.data.summary ?? xcode?.data.description ?? obsidian?.data.description ?? publiccode?.data.description?.shortDescription ?? cm?.data.description ?? github?.data.description;
63
+ const version = package_?.data.version ?? pyproject?.data.project?.version ?? poetry?.version ?? setupPy?.data.version ?? setupConfig?.data.version ?? pkgInfo?.data.version ?? cargo?.data.version ?? gem?.data.version ?? pom?.data.version ?? arduino?.data.version ?? processing?.data.prettyVersion ?? ofInstall?.data.version ?? cinder?.data.version ?? xcode?.data.version ?? obsidian?.data.version ?? publiccode?.data.softwareVersion ?? cm?.data.version ?? cm?.data.softwareVersion;
64
64
  const identifier = pom?.data.identifier ?? cinder?.data.id ?? obsidian?.data.id ?? xcode?.data.identifier ?? package_?.data.name ?? cm?.data.identifier;
65
65
  const ecosystemAuthors = [
66
66
  ...package_?.data.author ? [toPersonOrOrgLd({
@@ -68,17 +68,17 @@ const codemeta = defineTemplate(({ arduinoLibraryProperties, cinderCinderblockXm
68
68
  name: package_.data.author.name,
69
69
  url: package_.data.author.url
70
70
  })] : [],
71
- ...(pyproject?.data.project?.authors ?? []).map((a) => is.plainObject(a) ? toPersonOrOrgLd({
71
+ ...(pyproject?.data.project?.authors ?? []).map((a) => toPersonOrOrgLd(is.plainObject(a) ? {
72
72
  email: a.email,
73
73
  name: a.name
74
- }) : toPersonOrOrgLd({ name: a })),
75
- ...setupPy?.data.author ? [toPersonOrOrgLd({
74
+ } : { name: a })),
75
+ ...setupPy?.data.author !== void 0 && setupPy.data.author !== "" ? [toPersonOrOrgLd({
76
76
  email: setupPy.data.author_email,
77
77
  name: setupPy.data.author
78
78
  })] : [],
79
- ...setupCfg?.data.author ? [toPersonOrOrgLd({
80
- email: setupCfg.data.author_email,
81
- name: setupCfg.data.author
79
+ ...setupConfig?.data.author !== void 0 && setupConfig.data.author !== "" ? [toPersonOrOrgLd({
80
+ email: setupConfig.data.author_email,
81
+ name: setupConfig.data.author
82
82
  })] : [],
83
83
  ...(cargo?.data.authors ?? []).map((a) => toPersonOrOrgLd({
84
84
  email: a.email,
@@ -99,14 +99,14 @@ const codemeta = defineTemplate(({ arduinoLibraryProperties, cinderCinderblockXm
99
99
  name: a.name,
100
100
  url: a.url
101
101
  })),
102
- ...ofAddon?.data.author ? [toPersonOrOrgLd({ name: ofAddon.data.author })] : [],
103
- ...ofInstall?.data.author ? [toPersonOrOrgLd({ name: ofInstall.data.author })] : [],
102
+ ...ofAddon?.data.author !== void 0 && ofAddon.data.author !== "" ? [toPersonOrOrgLd({ name: ofAddon.data.author })] : [],
103
+ ...ofInstall?.data.author !== void 0 && ofInstall.data.author !== "" ? [toPersonOrOrgLd({ name: ofInstall.data.author })] : [],
104
104
  ...(cinder?.data.author ?? []).map((a) => toPersonOrOrgLd({ name: a })),
105
- ...xcode?.data.author ? [toPersonOrOrgLd({
105
+ ...xcode?.data.author !== void 0 && xcode.data.author !== "" ? [toPersonOrOrgLd({
106
106
  email: xcode.data.authorEmail,
107
107
  name: xcode.data.author
108
108
  })] : [],
109
- ...obsidian?.data.author ? [toPersonOrOrgLd({
109
+ ...obsidian?.data.author !== void 0 && obsidian.data.author !== "" ? [toPersonOrOrgLd({
110
110
  name: obsidian.data.author,
111
111
  url: obsidian.data.authorUrl
112
112
  })] : [],
@@ -127,11 +127,11 @@ const codemeta = defineTemplate(({ arduinoLibraryProperties, cinderCinderblockXm
127
127
  url: p.url
128
128
  }));
129
129
  const author = resolvePersonsOrOrgs(ecosystemAuthors, cmAuthors);
130
- const ecosystemContributors = [...collectArrayField(nodePackageJson, (d) => d.contributors?.map((c) => is.plainObject(c) ? toPersonOrOrgLd({
130
+ const ecosystemContributors = [...collectArrayField(nodePackageJson, (d) => d.contributors?.map((c) => toPersonOrOrgLd(is.plainObject(c) ? {
131
131
  email: c.email,
132
132
  name: c.name,
133
133
  url: c.url
134
- }) : toPersonOrOrgLd({ name: c }))), ...(pom?.data.contributors ?? []).map((c) => toPersonOrOrgLd({
134
+ } : { name: c }))), ...(pom?.data.contributors ?? []).map((c) => toPersonOrOrgLd({
135
135
  affiliation: c.organization,
136
136
  email: c.email,
137
137
  name: c.name,
@@ -149,24 +149,24 @@ const codemeta = defineTemplate(({ arduinoLibraryProperties, cinderCinderblockXm
149
149
  }));
150
150
  const contributor = resolvePersonsOrOrgs(ecosystemContributors, cmContributors);
151
151
  const ecosystemMaintainers = [
152
- ...collectArrayField(nodePackageJson, (d) => d.maintainers?.map((c) => is.plainObject(c) ? toPersonOrOrgLd({
152
+ ...collectArrayField(nodePackageJson, (d) => d.maintainers?.map((c) => toPersonOrOrgLd(is.plainObject(c) ? {
153
153
  email: c.email,
154
154
  name: c.name,
155
155
  url: c.url
156
- }) : toPersonOrOrgLd({ name: c }))),
157
- ...(pyproject?.data.project?.maintainers ?? []).map((m) => is.plainObject(m) ? toPersonOrOrgLd({
156
+ } : { name: c }))),
157
+ ...(pyproject?.data.project?.maintainers ?? []).map((m) => toPersonOrOrgLd(is.plainObject(m) ? {
158
158
  email: m.email,
159
159
  name: m.name
160
- }) : toPersonOrOrgLd({ name: m })),
161
- ...setupPy?.data.maintainer ? [toPersonOrOrgLd({
160
+ } : { name: m })),
161
+ ...setupPy?.data.maintainer !== void 0 && setupPy.data.maintainer !== "" ? [toPersonOrOrgLd({
162
162
  email: setupPy.data.maintainer_email,
163
163
  name: setupPy.data.maintainer
164
164
  })] : [],
165
- ...setupCfg?.data.maintainer ? [toPersonOrOrgLd({
166
- email: setupCfg.data.maintainer_email,
167
- name: setupCfg.data.maintainer
165
+ ...setupConfig?.data.maintainer !== void 0 && setupConfig.data.maintainer !== "" ? [toPersonOrOrgLd({
166
+ email: setupConfig.data.maintainer_email,
167
+ name: setupConfig.data.maintainer
168
168
  })] : [],
169
- ...pkgInfo?.data.maintainer ? [toPersonOrOrgLd({
169
+ ...pkgInfo?.data.maintainer !== void 0 && pkgInfo.data.maintainer !== "" ? [toPersonOrOrgLd({
170
170
  email: pkgInfo.data.maintainer_email,
171
171
  name: pkgInfo.data.maintainer
172
172
  })] : [],
@@ -186,7 +186,7 @@ const codemeta = defineTemplate(({ arduinoLibraryProperties, cinderCinderblockXm
186
186
  url: p.url
187
187
  }));
188
188
  const maintainer = resolvePersonsOrOrgs(ecosystemMaintainers, cmMaintainers);
189
- const ecosystemCopyrightHolders = [...publiccode?.data.mainCopyrightOwner ? [toPersonOrOrgLd({ name: publiccode.data.mainCopyrightOwner })] : [], ...xcode?.data.copyrightHolder ? [toPersonOrOrgLd({ name: xcode.data.copyrightHolder })] : []];
189
+ const ecosystemCopyrightHolders = [...publiccode?.data.mainCopyrightOwner !== void 0 && publiccode.data.mainCopyrightOwner !== "" ? [toPersonOrOrgLd({ name: publiccode.data.mainCopyrightOwner })] : [], ...xcode?.data.copyrightHolder !== void 0 && xcode.data.copyrightHolder !== "" ? [toPersonOrOrgLd({ name: xcode.data.copyrightHolder })] : []];
190
190
  const cmCopyrightHolders = cm?.data.copyrightHolder?.map((p) => toPersonOrOrgLd({
191
191
  affiliation: p.affiliation,
192
192
  email: p.email,
@@ -208,16 +208,16 @@ const codemeta = defineTemplate(({ arduinoLibraryProperties, cinderCinderblockXm
208
208
  type: p.type,
209
209
  url: p.url
210
210
  })));
211
- const codeRepository = github?.data.url ?? cargo?.data.repository ?? pom?.data.scmUrl ?? goMod?.data.repository_url ?? publiccode?.data.url ?? arduino?.data.repository ?? cinder?.data.git ?? cm?.data.codeRepository ?? repositoryUrlFromPackageJson(package_?.data.repository) ?? caseInsensitiveLookup(pyproject?.data.project?.urls, "repository") ?? poetry?.repository;
212
- const programmingLanguage = nonEmpty([...github?.data.primaryLanguage ? [github.data.primaryLanguage] : [], ...cm?.data.programmingLanguage ?? []]) ?? nonEmpty(Object.keys(github?.data.languages ?? {})) ?? loc?.data.total?.languages.slice(0, 1);
211
+ const codeRepo = github?.data.url ?? cargo?.data.repository ?? pom?.data.scmUrl ?? goMod?.data.repository_url ?? publiccode?.data.url ?? arduino?.data.repository ?? cinder?.data.git ?? cm?.data.codeRepository ?? repoUrlFromPackageJson(package_?.data.repository) ?? caseInsensitiveLookup(pyproject?.data.project?.urls, "repository") ?? poetry?.repository;
212
+ const programmingLanguage = nonEmpty([...github?.data.primaryLanguage !== void 0 && github.data.primaryLanguage !== "" ? [github.data.primaryLanguage] : [], ...cm?.data.programmingLanguage ?? []]) ?? nonEmpty(Object.keys(github?.data.languages ?? {})) ?? loc?.data.total?.languages.slice(0, 1);
213
213
  const runtimePlatform = nonEmpty([
214
214
  ...Object.keys(package_?.data.engines ?? {}),
215
- ...goMod?.data.go_version ? [`go ${goMod.data.go_version}`] : [],
216
- ...cargo?.data.rustVersion ? [`rust ${cargo.data.rustVersion}`] : [],
217
- ...pyproject?.data.project?.requiresPython ? [`python ${pyproject.data.project.requiresPython}`] : [],
218
- ...setupPy?.data.python_requires ? [`python ${setupPy.data.python_requires}`] : [],
219
- ...gem?.data.required_ruby_version ? [`ruby ${gem.data.required_ruby_version}`] : [],
220
- ...pom?.data.javaVersion ? [`java ${pom.data.javaVersion}`] : [],
215
+ ...goMod?.data.go_version !== void 0 && goMod.data.go_version !== "" ? [`go ${goMod.data.go_version}`] : [],
216
+ ...cargo?.data.rustVersion !== void 0 && cargo.data.rustVersion !== "" ? [`rust ${cargo.data.rustVersion}`] : [],
217
+ ...pyproject?.data.project?.requiresPython !== void 0 && pyproject.data.project.requiresPython !== "" ? [`python ${pyproject.data.project.requiresPython}`] : [],
218
+ ...setupPy?.data.python_requires !== void 0 && setupPy.data.python_requires !== "" ? [`python ${setupPy.data.python_requires}`] : [],
219
+ ...gem?.data.required_ruby_version !== void 0 && gem.data.required_ruby_version !== "" ? [`ruby ${gem.data.required_ruby_version}`] : [],
220
+ ...pom?.data.javaVersion !== void 0 && pom.data.javaVersion !== "" ? [`java ${pom.data.javaVersion}`] : [],
221
221
  ...cm?.data.runtimePlatform ?? []
222
222
  ]);
223
223
  const operatingSystem = nonEmpty([
@@ -227,9 +227,9 @@ const codemeta = defineTemplate(({ arduinoLibraryProperties, cinderCinderblockXm
227
227
  ...cinder?.data.supports ?? [],
228
228
  ...xcode?.data.operatingSystems ?? []
229
229
  ]);
230
- const applicationCategory = cm?.data.applicationCategory ?? xcode?.data.applicationCategory ?? arduino?.data.category ?? publiccode?.data.softwareType;
231
- const applicationSubCategory = cm?.data.applicationSubCategory;
232
- const runtimeDeps = collectRuntimeDeps({
230
+ const appCategory = cm?.data.applicationCategory ?? xcode?.data.applicationCategory ?? arduino?.data.category ?? publiccode?.data.softwareType;
231
+ const appSubCategory = cm?.data.applicationSubCategory;
232
+ const runtimeDependencies = collectRuntimeDependencies({
233
233
  arduino,
234
234
  cargo,
235
235
  cinder,
@@ -243,23 +243,23 @@ const codemeta = defineTemplate(({ arduinoLibraryProperties, cinderCinderblockXm
243
243
  publiccode,
244
244
  pyproject,
245
245
  rubyGemspec,
246
- setupCfg,
246
+ setupCfg: setupConfig,
247
247
  setupPy
248
248
  });
249
- const softwareRequirements = runtimeDeps.length > 0 ? deduplicateDependencies(runtimeDeps) : cm?.data.softwareRequirements?.map((d) => toDependencyLd(d.name ?? d.identifier ?? "", d.version, d.identifier, d.runtimePlatform));
250
- const developmentDeps = collectDevelopmentDeps({
249
+ const softwareRequirements = runtimeDependencies.length > 0 ? deduplicateDependencies(runtimeDependencies) : cm?.data.softwareRequirements?.map((d) => toDependencyLd(d.name ?? d.identifier ?? "", d.version, d.identifier, d.runtimePlatform));
250
+ const developmentDependencies = collectDevelopmentDependencies({
251
251
  cargo,
252
252
  gem,
253
253
  javaPomXml,
254
254
  nodePackageJson,
255
255
  rubyGemspec
256
256
  });
257
- const softwareSuggestions = developmentDeps.length > 0 ? deduplicateDependencies(developmentDeps) : cm?.data.softwareSuggestions?.map((d) => toDependencyLd(d.name ?? d.identifier ?? "", d.version, d.identifier, d.runtimePlatform));
257
+ const softwareSuggestions = developmentDependencies.length > 0 ? deduplicateDependencies(developmentDependencies) : cm?.data.softwareSuggestions?.map((d) => toDependencyLd(d.name ?? d.identifier ?? "", d.version, d.identifier, d.runtimePlatform));
258
258
  const dateCreated = git?.data.commitDateFirst ?? github?.data.createdAt ?? cm?.data.dateCreated;
259
259
  const dateModified = git?.data.commitDateLast ?? github?.data.pushedAt ?? cm?.data.dateModified;
260
260
  const datePublished = npm?.data.publishDateLatest ?? pypi?.data.publishDateLatest ?? publiccode?.data.releaseDate ?? github?.data.releaseDateLatest ?? git?.data.tagVersionDateLatest ?? cm?.data.datePublished;
261
261
  const copyrightYear = xcode?.data.copyrightYear ?? pom?.data.inceptionYear ?? (cm?.data.copyrightYear === void 0 ? void 0 : String(cm.data.copyrightYear));
262
- const rawLicense = package_?.data.license ?? cargo?.data.license ?? resolvePythonLicense(pyproject?.data.project?.license) ?? setupPy?.data.license ?? setupCfg?.data.license ?? gem?.data.license ?? firstPomLicense(pom) ?? arduino?.data.license ?? cinder?.data.license ?? publiccode?.data.license ?? github?.data.licenseSpdxId ?? collectField(licenseFile, (d) => d.match?.spdxId)[0] ?? resolveCmLicense(cm?.data.license);
262
+ const rawLicense = package_?.data.license ?? cargo?.data.license ?? resolvePythonLicense(pyproject?.data.project?.license) ?? setupPy?.data.license ?? setupConfig?.data.license ?? gem?.data.license ?? firstPomLicense(pom) ?? arduino?.data.license ?? cinder?.data.license ?? publiccode?.data.license ?? github?.data.licenseSpdxId ?? collectField(licenseFile, (d) => d.match?.spdxId)[0] ?? resolveCmLicense(cm?.data.license);
263
263
  const license = is.nonEmptyStringAndNotWhitespace(rawLicense) ? isProprietaryLicenseSentinel(rawLicense) ? rawLicense : toSpdxLicenseUrl(rawLicense) : void 0;
264
264
  const isAccessibleForFree = cm?.data.isAccessibleForFree ?? (github?.data.isPrivate === false ? true : void 0);
265
265
  const keywords = nonEmpty(deduplicateStrings([
@@ -267,7 +267,7 @@ const codemeta = defineTemplate(({ arduinoLibraryProperties, cinderCinderblockXm
267
267
  ...pyproject?.data.project?.keywords ?? [],
268
268
  ...poetry?.keywords ?? [],
269
269
  ...setupPy?.data.keywords ?? [],
270
- ...setupCfg?.data.keywords ?? [],
270
+ ...setupConfig?.data.keywords ?? [],
271
271
  ...pkgInfo?.data.keywords ?? [],
272
272
  ...cargo?.data.keywords ?? [],
273
273
  ...ofAddon?.data.tags ?? [],
@@ -275,7 +275,7 @@ const codemeta = defineTemplate(({ arduinoLibraryProperties, cinderCinderblockXm
275
275
  ...github?.data.topics ?? [],
276
276
  ...cm?.data.keywords ?? []
277
277
  ]));
278
- const url = stripReadmeFragment(package_?.data.homepage) ?? caseInsensitiveLookup(pyproject?.data.project?.urls, "homepage") ?? poetry?.homepage ?? cargo?.data.homepage ?? setupPy?.data.url ?? setupCfg?.data.url ?? pkgInfo?.data.home_page ?? gem?.data.homepage ?? pom?.data.url ?? arduino?.data.url ?? processing?.data.url ?? ofAddon?.data.url ?? ofInstall?.data.siteUrl ?? cinder?.data.url ?? xcode?.data.url ?? publiccode?.data.landingUrl ?? github?.data.homepageUrl ?? cm?.data.url ?? caseInsensitiveLookup(pyproject?.data.project?.urls, "repository") ?? poetry?.repository;
278
+ const url = stripReadmeFragment(package_?.data.homepage) ?? caseInsensitiveLookup(pyproject?.data.project?.urls, "homepage") ?? poetry?.homepage ?? cargo?.data.homepage ?? setupPy?.data.url ?? setupConfig?.data.url ?? pkgInfo?.data.home_page ?? gem?.data.homepage ?? pom?.data.url ?? arduino?.data.url ?? processing?.data.url ?? ofAddon?.data.url ?? ofInstall?.data.siteUrl ?? cinder?.data.url ?? xcode?.data.url ?? publiccode?.data.landingUrl ?? github?.data.homepageUrl ?? cm?.data.url ?? caseInsensitiveLookup(pyproject?.data.project?.urls, "repository") ?? poetry?.repository;
279
279
  const downloadUrl = ofInstall?.data.downloadUrl ?? processing?.data.download ?? npm?.data.url ?? pypi?.data.url ?? cm?.data.downloadUrl;
280
280
  const issueTracker = bugsUrlFromPackageJson(package_?.data.bugs) ?? pom?.data.issueManagementUrl ?? cm?.data.issueTracker ?? (github?.data.hasIssuesEnabled ? `${github.data.url}/issues` : void 0);
281
281
  const continuousIntegration = pom?.data.ciManagementUrl ?? cm?.data.continuousIntegration;
@@ -283,22 +283,22 @@ const codemeta = defineTemplate(({ arduinoLibraryProperties, cinderCinderblockXm
283
283
  const developmentStatus = publiccode?.data.developmentStatus ?? cm?.data.developmentStatus;
284
284
  const funding = cm?.data.funding;
285
285
  const buildInstructions = cm?.data.buildInstructions;
286
- const readme = readmeUrl(firstOf(readmeFile), codeRepository, github?.data.defaultBranch ?? git?.data.branchCurrent, firstOf(metascope)?.data.options.path) ?? cm?.data.readme;
286
+ const readme = readmeUrl(firstOf(readmeFile), codeRepo, github?.data.defaultBranch ?? git?.data.branchCurrent, firstOf(metascope)?.data.options.path) ?? cm?.data.readme;
287
287
  const releaseNotes = cm?.data.releaseNotes;
288
288
  const installUrl = cm?.data.installUrl;
289
289
  const relatedLink = cm?.data.relatedLink;
290
290
  return stripUndefined({
291
291
  "@context": "https://w3id.org/codemeta/3.0",
292
292
  "@type": "SoftwareSourceCode",
293
- applicationCategory,
294
- applicationSubCategory,
293
+ applicationCategory: appCategory,
294
+ applicationSubCategory: appSubCategory,
295
295
  author,
296
296
  buildInstructions,
297
- codeRepository,
297
+ codeRepository: codeRepo,
298
298
  continuousIntegration,
299
299
  contributor,
300
300
  copyrightHolder,
301
- copyrightYear: is.nonEmptyStringAndNotWhitespace(copyrightYear) ? Number.parseInt(copyrightYear, 10) || void 0 : void 0,
301
+ copyrightYear: toCopyrightYear(copyrightYear),
302
302
  dateCreated: toDateOnly(dateCreated),
303
303
  dateModified: toDateOnly(dateModified),
304
304
  datePublished: toDateOnly(datePublished),
@@ -354,62 +354,62 @@ function resolvePersonsOrOrgs(ecosystemPersons, fallbackPersons) {
354
354
  /**
355
355
  * Collect runtime dependencies from all ecosystem sources.
356
356
  */
357
- function collectRuntimeDeps(sources) {
358
- const deps = [];
359
- deps.push(...collectArrayField(sources.nodePackageJson, (d) => objectEntriesToDeps(d.dependencies)));
360
- deps.push(...(sources.pyproject?.data.project?.dependencies ?? []).map((dep) => parsePep508Dep(dep)));
361
- deps.push(...(sources.setupPy?.data.install_requires ?? []).map((dep) => parsePep508Dep(dep)));
362
- deps.push(...(sources.setupCfg?.data.install_requires ?? []).map((dep) => parsePep508Dep(dep)));
363
- deps.push(...(sources.pkgInfo?.data.requires_dist ?? []).map((dep) => parsePep508Dep(dep)));
364
- deps.push(...(sources.cargo?.data.dependencies ?? []).map((d) => toDependencyLd(d.name, d.version)));
365
- deps.push(...collectArrayField(sources.rubyGemspec, (d) => d.dependencies.filter((dep) => dep.type === "runtime").map((dep) => toDependencyLd(dep.name, dep.requirements.join(", ")))));
366
- deps.push(...collectArrayField(sources.javaPomXml, (d) => d.dependencies.map((dep) => toDependencyLd(dep.artifactId, dep.version, `${dep.groupId}:${dep.artifactId}`))));
367
- deps.push(...collectArrayField(sources.goGoMod, (d) => d.dependencies.map((dep) => toDependencyLd(dep.module, dep.version))));
368
- deps.push(...(sources.arduino?.data.depends ?? []).map((d) => toDependencyLd(d.name, d.versionConstraint)));
369
- deps.push(...(sources.ofAddon?.data.dependencies ?? []).map((d) => toDependencyLd(d)));
370
- deps.push(...(sources.ofInstall?.data.requirements ?? []).map((d) => toDependencyLd(d)));
371
- deps.push(...(sources.cinder?.data.requires ?? []).map((d) => toDependencyLd(d)));
372
- deps.push(...(sources.publiccode?.data.dependencies ?? []).map((d) => toDependencyLd(d.name, d.version)));
373
- return deps;
357
+ function collectRuntimeDependencies(sources) {
358
+ const dependencies = [];
359
+ dependencies.push(...collectArrayField(sources.nodePackageJson, (d) => objectEntriesToDependencies(d.dependencies)));
360
+ dependencies.push(...(sources.pyproject?.data.project?.dependencies ?? []).map((dependency) => parsePep508Dependency(dependency)));
361
+ dependencies.push(...(sources.setupPy?.data.install_requires ?? []).map((dependency) => parsePep508Dependency(dependency)));
362
+ dependencies.push(...(sources.setupCfg?.data.install_requires ?? []).map((dependency) => parsePep508Dependency(dependency)));
363
+ dependencies.push(...(sources.pkgInfo?.data.requires_dist ?? []).map((dependency) => parsePep508Dependency(dependency)));
364
+ dependencies.push(...(sources.cargo?.data.dependencies ?? []).map((d) => toDependencyLd(d.name, d.version)));
365
+ dependencies.push(...collectArrayField(sources.rubyGemspec, (d) => d.dependencies.filter((dependency) => dependency.type === "runtime").map((dependency) => toDependencyLd(dependency.name, dependency.requirements.join(", ")))));
366
+ dependencies.push(...collectArrayField(sources.javaPomXml, (d) => d.dependencies.map((dependency) => toDependencyLd(dependency.artifactId, dependency.version, `${dependency.groupId}:${dependency.artifactId}`))));
367
+ dependencies.push(...collectArrayField(sources.goGoMod, (d) => d.dependencies.map((dependency) => toDependencyLd(dependency.module, dependency.version))));
368
+ dependencies.push(...(sources.arduino?.data.depends ?? []).map((d) => toDependencyLd(d.name, d.versionConstraint)));
369
+ dependencies.push(...(sources.ofAddon?.data.dependencies ?? []).map((d) => toDependencyLd(d)));
370
+ dependencies.push(...(sources.ofInstall?.data.requirements ?? []).map((d) => toDependencyLd(d)));
371
+ dependencies.push(...(sources.cinder?.data.requires ?? []).map((d) => toDependencyLd(d)));
372
+ dependencies.push(...(sources.publiccode?.data.dependencies ?? []).map((d) => toDependencyLd(d.name, d.version)));
373
+ return dependencies;
374
374
  }
375
375
  /**
376
376
  * Collect dev dependencies from ecosystem sources.
377
377
  */
378
- function collectDevelopmentDeps(sources) {
379
- const deps = [];
380
- deps.push(...collectArrayField(sources.nodePackageJson, (d) => objectEntriesToDeps(d.devDependencies)));
381
- deps.push(...(sources.cargo?.data.devDependencies ?? []).map((d) => toDependencyLd(d.name, d.version)));
382
- deps.push(...collectArrayField(sources.rubyGemspec, (d) => d.dependencies.filter((dep) => dep.type === "development").map((dep) => toDependencyLd(dep.name, dep.requirements.join(", ")))));
383
- deps.push(...collectArrayField(sources.javaPomXml, (d) => d.devDependencies.map((dep) => toDependencyLd(dep.artifactId, dep.version, `${dep.groupId}:${dep.artifactId}`))));
384
- return deps;
378
+ function collectDevelopmentDependencies(sources) {
379
+ const dependencies = [];
380
+ dependencies.push(...collectArrayField(sources.nodePackageJson, (d) => objectEntriesToDependencies(d.devDependencies)));
381
+ dependencies.push(...(sources.cargo?.data.devDependencies ?? []).map((d) => toDependencyLd(d.name, d.version)));
382
+ dependencies.push(...collectArrayField(sources.rubyGemspec, (d) => d.dependencies.filter((dependency) => dependency.type === "development").map((dependency) => toDependencyLd(dependency.name, dependency.requirements.join(", ")))));
383
+ dependencies.push(...collectArrayField(sources.javaPomXml, (d) => d.devDependencies.map((dependency) => toDependencyLd(dependency.artifactId, dependency.version, `${dependency.groupId}:${dependency.artifactId}`))));
384
+ return dependencies;
385
385
  }
386
386
  /**
387
387
  * Convert a Record<name, version> dependency map to CodemetaDependencyLd[].
388
388
  */
389
- function objectEntriesToDeps(deps) {
390
- if (deps === void 0) return;
391
- return Object.entries(deps).map(([depName, depVersion]) => toDependencyLd(depName, depVersion));
389
+ function objectEntriesToDependencies(dependencies) {
390
+ if (dependencies === void 0) return;
391
+ return Object.entries(dependencies).map(([dependencyName, dependencyVersion]) => toDependencyLd(dependencyName, dependencyVersion));
392
392
  }
393
393
  /**
394
394
  * Parse a PEP 508 dependency string ("package>=1.0") into a
395
395
  * CodemetaDependencyLd.
396
396
  */
397
- function parsePep508Dep(dep) {
398
- const trimmed = dep.trim();
397
+ function parsePep508Dependency(dependency) {
398
+ const trimmed = dependency.trim();
399
399
  const nameMatch = PEP508_NAME_REGEX.exec(trimmed);
400
400
  if (nameMatch) {
401
- const depVersion = trimmed.slice(nameMatch[0].length).trim();
402
- return toDependencyLd(nameMatch[0], depVersion.length > 0 ? depVersion : void 0);
401
+ const dependencyVersion = trimmed.slice(nameMatch[0].length).trim();
402
+ return toDependencyLd(nameMatch[0], dependencyVersion.length > 0 ? dependencyVersion : void 0);
403
403
  }
404
404
  return toDependencyLd(trimmed);
405
405
  }
406
406
  /**
407
407
  * Extract URL from package.json repository field (string or {url}).
408
408
  */
409
- function repositoryUrlFromPackageJson(repository) {
410
- if (repository === void 0) return;
411
- if (typeof repository === "string") return repository;
412
- return repository.url;
409
+ function repoUrlFromPackageJson(repo) {
410
+ if (repo === void 0) return;
411
+ if (typeof repo === "string") return repo;
412
+ return repo.url;
413
413
  }
414
414
  /**
415
415
  * Extract URL from package.json bugs field.
@@ -442,6 +442,15 @@ function resolveCmLicense(cmLicense) {
442
442
  return Array.isArray(cmLicense) ? cmLicense[0] : cmLicense;
443
443
  }
444
444
  /**
445
+ * Parse a copyright year string to a leading integer year, or undefined when it
446
+ * contains no parseable year (0 and NaN both collapse to undefined).
447
+ */
448
+ function toCopyrightYear(copyrightYear) {
449
+ if (!is.nonEmptyStringAndNotWhitespace(copyrightYear)) return;
450
+ const year = Number.parseInt(copyrightYear, 10);
451
+ return year === 0 || Number.isNaN(year) ? void 0 : year;
452
+ }
453
+ /**
445
454
  * Deduplicate strings case-insensitively, preserving first occurrence casing.
446
455
  */
447
456
  function deduplicateStrings(strings) {
@@ -450,7 +459,7 @@ function deduplicateStrings(strings) {
450
459
  const key = s.toLowerCase().trim();
451
460
  if (key.length > 0 && !seen.has(key)) seen.set(key, s);
452
461
  }
453
- return [...seen.values()];
462
+ return seen.values().toArray();
454
463
  }
455
464
  /**
456
465
  * Case-insensitive lookup in a string record (e.g. pyproject.toml
@@ -1,4 +1,5 @@
1
1
  import { Template } from "../metadata-types.js";
2
+ import "../index.js";
2
3
  //#region src/lib/templates/frontmatter.d.ts
3
4
  type TemplateDataFrontmatter = ReturnType<typeof frontmatter>;
4
5
  /**
@@ -18,7 +19,7 @@ declare const frontmatter: Template<{
18
19
  Public: boolean;
19
20
  Fork: boolean;
20
21
  Published: boolean;
21
- Status: "unknown" | "maintainer" | "author" | "observer";
22
+ Status: "author" | "maintainer" | "unknown" | "observer";
22
23
  Tags: string[] | null;
23
24
  Aliases: (string | null | undefined)[] | null;
24
25
  License: string[] | null;
@@ -70,7 +70,7 @@ const frontmatter = defineTemplate((context, templateData) => {
70
70
  Forks: github?.forkCount ?? null,
71
71
  "Downloads Total": nodeNpmRegistry?.downloadsTotal ?? obsidianPluginRegistry?.downloadCount ?? pythonPypiRegistry?.downloads180Days ?? github?.releaseDownloadCount ?? null,
72
72
  "Downloads Monthly": nodeNpmRegistry?.downloadsMonthly ?? pythonPypiRegistry?.downloadsMonthly ?? null,
73
- CI: ciStatus ? titleCase(ciStatus) : null,
73
+ CI: ciStatus === void 0 ? null : titleCase(ciStatus),
74
74
  "CI URL": ciUrl ?? null,
75
75
  Releases: github?.releaseCount ?? gitStats?.tagReleaseCount ?? null,
76
76
  "Issues Open": github?.issueCountOpen ?? null,
@@ -5,6 +5,7 @@ import { TemplateDataCodemetaJson } from "./codemeta-json.js";
5
5
  import { TemplateDataFrontmatter } from "./frontmatter.js";
6
6
  import { TemplateDataMetadata } from "./metadata.js";
7
7
  import { TemplateDataProject } from "./project.js";
8
+ import "../index.js";
8
9
  //#region src/lib/templates/index.d.ts
9
10
  /**
10
11
  * Built-in templates, keyed by name.
@@ -157,7 +158,7 @@ declare const templates: {
157
158
  Public: boolean;
158
159
  Fork: boolean;
159
160
  Published: boolean;
160
- Status: "unknown" | "maintainer" | "author" | "observer";
161
+ Status: "author" | "maintainer" | "unknown" | "observer";
161
162
  Tags: string[] | null;
162
163
  Aliases: (string | null | undefined)[] | null;
163
164
  License: string[] | null;
@@ -18,7 +18,7 @@ const templates = {
18
18
  * Type guard
19
19
  */
20
20
  function isKeyOfTemplate(value) {
21
- return typeof value === "string" && value in templates;
21
+ return typeof value === "string" && Object.hasOwn(templates, value);
22
22
  }
23
23
  //#endregion
24
24
  export { isKeyOfTemplate, templates };
@@ -1,4 +1,5 @@
1
1
  import { Template } from "../metadata-types.js";
2
+ import "../index.js";
2
3
  //#region src/lib/templates/metadata.d.ts
3
4
  type TemplateDataMetadata = ReturnType<typeof metadata>;
4
5
  /**
@@ -1,4 +1,5 @@
1
1
  import { Template } from "../metadata-types.js";
2
+ import "../index.js";
2
3
  //#region src/lib/templates/project.d.ts
3
4
  type TemplateDataProject = ReturnType<typeof project>;
4
5
  /**
@@ -42,36 +42,37 @@ function toPersonOrOrgLd(options) {
42
42
  function deduplicatePersonsOrOrgs(persons) {
43
43
  const seen = /* @__PURE__ */ new Map();
44
44
  for (const person of persons) {
45
- const key = (person.name ?? ([person.givenName, person.familyName].filter(Boolean).join(" ") || void 0) ?? person.email ?? "").toLowerCase().trim();
45
+ const fullName = [person.givenName, person.familyName].filter(Boolean).join(" ");
46
+ const key = (person.name ?? (fullName === "" ? void 0 : fullName) ?? person.email ?? "").toLowerCase().trim();
46
47
  if (key.length > 0 && !seen.has(key)) seen.set(key, person);
47
48
  }
48
- const result = [...seen.values()];
49
+ const result = seen.values().toArray();
49
50
  return result.length > 0 ? result : void 0;
50
51
  }
51
52
  /**
52
53
  * Build a codemeta JSON-LD SoftwareApplication dependency node.
53
54
  */
54
55
  function toDependencyLd(name, version, identifier, runtimePlatform) {
55
- const dep = {
56
+ const dependency = {
56
57
  "@type": "SoftwareApplication",
57
58
  name
58
59
  };
59
- if (is.nonEmptyStringAndNotWhitespace(version)) dep.version = version;
60
- if (is.nonEmptyStringAndNotWhitespace(identifier)) dep.identifier = identifier;
61
- if (is.nonEmptyStringAndNotWhitespace(runtimePlatform)) dep.runtimePlatform = runtimePlatform;
62
- return dep;
60
+ if (is.nonEmptyStringAndNotWhitespace(version)) dependency.version = version;
61
+ if (is.nonEmptyStringAndNotWhitespace(identifier)) dependency.identifier = identifier;
62
+ if (is.nonEmptyStringAndNotWhitespace(runtimePlatform)) dependency.runtimePlatform = runtimePlatform;
63
+ return dependency;
63
64
  }
64
65
  /**
65
66
  * Deduplicate dependencies by name (case-insensitive). Keeps the first
66
67
  * occurrence. Returns undefined if the result is empty.
67
68
  */
68
- function deduplicateDependencies(deps) {
69
+ function deduplicateDependencies(dependencies) {
69
70
  const seen = /* @__PURE__ */ new Map();
70
- for (const dep of deps) {
71
- const key = dep.name.toLowerCase().trim();
72
- if (key.length > 0 && !seen.has(key)) seen.set(key, dep);
71
+ for (const dependency of dependencies) {
72
+ const key = dependency.name.toLowerCase().trim();
73
+ if (key.length > 0 && !seen.has(key)) seen.set(key, dependency);
73
74
  }
74
- const result = [...seen.values()];
75
+ const result = seen.values().toArray();
75
76
  return result.length > 0 ? result : void 0;
76
77
  }
77
78
  /**
@@ -81,7 +82,7 @@ function deduplicateDependencies(deps) {
81
82
  function toSpdxLicenseUrl(spdxId) {
82
83
  return `https://spdx.org/licenses/${spdxId.replace("https://spdx.org/licenses/", "").replace("http://spdx.org/licenses/", "")}`;
83
84
  }
84
- const SEE_LICENSE_IN_REGEX = /^see license in /i;
85
+ const SEE_LICENSE_IN_REGEX = /^see license in /iv;
85
86
  /**
86
87
  * Detect npm package.json sentinel license values that are not SPDX
87
88
  * identifiers: `"UNLICENSED"` (proprietary, all rights reserved) and `"SEE
@@ -1,12 +1,28 @@
1
1
  import { log } from "../log.js";
2
2
  //#region src/lib/utilities/fetch.ts
3
+ const MIN_FETCH_INTERVAL_MS = 300;
4
+ let fetchChain = Promise.resolve();
5
+ let lastFetchStart = 0;
6
+ async function waitForFetchTurn() {
7
+ const previous = fetchChain;
8
+ const turn = (async () => {
9
+ await previous;
10
+ const wait = lastFetchStart + MIN_FETCH_INTERVAL_MS - Date.now();
11
+ if (wait > 0) await sleep(wait);
12
+ lastFetchStart = Date.now();
13
+ })();
14
+ fetchChain = turn;
15
+ return turn;
16
+ }
3
17
  /**
4
18
  * Fetch with automatic retries and exponential backoff. Retries on network
5
- * errors and 429/5xx responses.
19
+ * errors and 429/5xx responses. Request starts are paced globally to stay under
20
+ * per-IP rate limits.
6
21
  */
7
22
  async function fetchWithRetry(url, options, maxRetries = 5) {
8
23
  let lastError;
9
24
  for (let attempt = 0; attempt <= maxRetries; attempt++) try {
25
+ await waitForFetchTurn();
10
26
  const response = await fetch(url, options);
11
27
  if ((response.status === 429 || response.status >= 500) && attempt < maxRetries) {
12
28
  const delay = getDelay(attempt, response);
@@ -28,7 +44,7 @@ async function fetchWithRetry(url, options, maxRetries = 5) {
28
44
  function getDelay(attempt, response) {
29
45
  const backoff = 1e3 * 2 ** attempt;
30
46
  const retryAfter = response?.headers.get("retry-after");
31
- if (retryAfter) {
47
+ if (retryAfter !== null && retryAfter !== void 0 && retryAfter !== "") {
32
48
  const seconds = Number(retryAfter);
33
49
  if (!Number.isNaN(seconds)) return Math.max(seconds * 1e3, backoff);
34
50
  }
@@ -1,7 +1,7 @@
1
1
  import { DEFAULT_GET_METADATA_OPTIONS } from "../metadata-types.js";
2
2
  import { relative } from "node:path";
3
3
  //#region src/lib/utilities/formatting.ts
4
- const URL_PROTOCOL_REGEX = /^[a-z]+:\/\//i;
4
+ const URL_PROTOCOL_REGEX = /^[a-z]+:\/\//iv;
5
5
  /**
6
6
  * Format an absolute path as either absolute or relative, based on the
7
7
  * `absolute` option. When relative, paths identical to `basePath` are returned
@@ -16,10 +16,10 @@ function getGitHubRemoteFromConfig(remotes) {
16
16
  });
17
17
  for (const [, remote] of sorted) {
18
18
  const { url } = remote;
19
- if (!url) continue;
19
+ if (url === void 0 || url === "") continue;
20
20
  try {
21
21
  const parsed = gitUrlParse(url);
22
- if (parsed.source === "github.com" && parsed.owner && parsed.name) return {
22
+ if (parsed.source === "github.com" && parsed.owner !== "" && parsed.name !== "") return {
23
23
  owner: parsed.owner,
24
24
  repo: parsed.name
25
25
  };
@@ -15,10 +15,15 @@
15
15
  * - YAML front matter stripping
16
16
  */
17
17
  type LicenseMatch = {
18
- /** Dice coefficient confidence score (0–1). */confidence: number; /** Full license name (e.g. "MIT License", "Apache License 2.0"). */
19
- name: string; /** Whether the license is OSI approved. */
20
- osiApproved: boolean; /** SPDX license identifier (e.g. "MIT", "Apache-2.0"). */
21
- spdxId: string; /** SPDX license URL. */
18
+ /** Dice coefficient confidence score (0–1). */
19
+ confidence: number;
20
+ /** Full license name (e.g. "MIT License", "Apache License 2.0"). */
21
+ name: string;
22
+ /** Whether the license is OSI approved. */
23
+ osiApproved: boolean;
24
+ /** SPDX license identifier (e.g. "MIT", "Apache-2.0"). */
25
+ spdxId: string;
26
+ /** SPDX license URL. */
22
27
  spdxUrl: string;
23
28
  };
24
29
  //#endregion