@preventive/triage 1.0.0-alpha.5 → 1.0.0-alpha.51

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 (368) hide show
  1. package/LICENSE-APACHE +202 -0
  2. package/README.md +16 -0
  3. package/api/reap.ts +17 -0
  4. package/cli.js +6 -0
  5. package/client/dependency-paths.d.ts +3 -0
  6. package/client/dependency-paths.js +36 -0
  7. package/client/finding-link.js +305 -0
  8. package/client/linked-findings.d.ts +1 -0
  9. package/client/linked-findings.js +115 -0
  10. package/common/bundle-commit.d.ts +1 -0
  11. package/common/bundle-commit.js +4 -0
  12. package/common/bundle-compare-diff.d.ts +3 -0
  13. package/common/bundle-compare-diff.js +357 -0
  14. package/common/bundle-compare-inputs.d.ts +2 -0
  15. package/common/bundle-compare-inputs.js +65 -0
  16. package/common/bundle-compare-versions.js +172 -0
  17. package/common/bundle-comparison.d.ts +8 -0
  18. package/common/bundle-comparison.js +62 -0
  19. package/common/bundle-metadata.d.ts +15 -0
  20. package/common/bundle-metadata.js +226 -0
  21. package/common/bundle-pkg-of.d.ts +1 -0
  22. package/common/bundle-pkg-of.js +90 -0
  23. package/common/bundle-reasons.d.ts +2 -0
  24. package/common/bundle-reasons.js +21 -0
  25. package/common/bundle-source-links.d.ts +6 -0
  26. package/common/bundle-source-links.js +216 -0
  27. package/common/bundle-sourcemap.d.ts +10 -0
  28. package/common/bundle-sourcemap.js +100 -0
  29. package/common/bundle-sources.d.ts +7 -0
  30. package/common/bundle-sources.js +333 -0
  31. package/common/bundle-stats.js +41 -0
  32. package/common/bundle-tabs.js +1 -0
  33. package/common/code-language.js +65 -0
  34. package/common/default-scan-models.ts +31 -0
  35. package/common/dependency-reviews.d.ts +18 -0
  36. package/common/dependency-reviews.js +64 -0
  37. package/common/finding-duplicates.js +58 -0
  38. package/common/finding-filters.js +274 -0
  39. package/common/finding-id.js +47 -0
  40. package/common/github-issue-labels.d.ts +1 -0
  41. package/common/github-issue-labels.js +11 -0
  42. package/common/github-pr.ts +56 -0
  43. package/common/gzip.js +59 -0
  44. package/common/line-diff.d.ts +8 -0
  45. package/common/line-diff.js +57 -0
  46. package/common/linked-workspace-groups.js +63 -0
  47. package/common/managed/agent-tasks.ts +292 -0
  48. package/common/managed/comments.ts +34 -0
  49. package/common/managed/finding-detail.d.ts +3 -0
  50. package/common/managed/finding-detail.js +66 -0
  51. package/common/managed/github-agents.ts +114 -0
  52. package/common/managed/github-logins.ts +49 -0
  53. package/common/managed/npm-packages.d.ts +7 -0
  54. package/common/managed/npm-packages.js +51 -0
  55. package/common/managed/permissions.ts +31 -0
  56. package/common/managed/report-content.ts +72 -0
  57. package/common/managed/report-filter.ts +109 -0
  58. package/common/managed/repository-alias.ts +65 -0
  59. package/common/managed/roles.ts +35 -0
  60. package/common/managed/routes.d.ts +2 -0
  61. package/common/managed/routes.js +359 -0
  62. package/common/managed/scan-models.ts +6 -0
  63. package/common/managed/team-app.d.ts +6 -0
  64. package/common/managed/team-app.js +47 -0
  65. package/common/managed/triage.ts +83 -0
  66. package/common/managed/upload-seal.ts +126 -0
  67. package/common/pretty-print.d.ts +5 -0
  68. package/common/pretty-print.js +24 -0
  69. package/common/revalidation-conflicts.js +48 -0
  70. package/common/revalidation-input-groups.js +56 -0
  71. package/common/save-error-reason.ts +20 -7
  72. package/common/scan-server.ts +13 -0
  73. package/common/security-links.d.ts +5 -0
  74. package/common/security-links.js +33 -0
  75. package/common/server-info.ts +46 -0
  76. package/common/utf8.d.ts +3 -0
  77. package/common/utf8.js +45 -0
  78. package/common/workspace-app.js +39 -0
  79. package/common/workspace-groups.js +176 -0
  80. package/out/activity.js +4 -0
  81. package/out/brotli-fallback.js +3 -3
  82. package/out/chunk-2K6UN3PC.js +3 -0
  83. package/out/chunk-2XUEZHIP.js +28 -0
  84. package/out/chunk-2ZDZWLFY.js +1 -0
  85. package/out/chunk-4HI2MSAV.js +17 -0
  86. package/out/chunk-6LNQTKAV.js +52 -0
  87. package/out/chunk-7BKKYVWR.js +7 -0
  88. package/out/chunk-7C7DWL7I.js +1 -0
  89. package/out/chunk-7JBSSRD7.js +3 -0
  90. package/out/chunk-7VAWINQF.js +6 -0
  91. package/out/chunk-C4Z6ZBXV.js +10 -0
  92. package/out/chunk-FMXWXDR4.js +36 -0
  93. package/out/chunk-GDPCLSOS.js +2 -0
  94. package/out/chunk-GO7FT4OD.js +1 -0
  95. package/out/chunk-HT2SSA3V.js +1 -0
  96. package/out/chunk-IPAH4LKF.js +232 -0
  97. package/out/chunk-IYPY47MC.js +1 -0
  98. package/out/chunk-IZ7POWI3.js +18 -0
  99. package/out/chunk-JCIIBLKR.js +1 -0
  100. package/out/chunk-KOGNQDSJ.js +1 -0
  101. package/out/chunk-NRPC74AM.js +1 -0
  102. package/out/chunk-OOJM4CTU.js +1 -0
  103. package/out/chunk-PBZPYIG7.js +1 -0
  104. package/out/chunk-PNHBZGDD.js +1 -0
  105. package/out/chunk-PWFUOLZK.js +17 -0
  106. package/out/chunk-PXK7EU4V.js +1 -0
  107. package/out/chunk-QB4CBOX2.js +1 -0
  108. package/out/chunk-R3NZFXVR.js +1 -0
  109. package/out/chunk-R5O6FAW4.js +2 -0
  110. package/out/chunk-SXV2T5C4.js +80 -0
  111. package/out/chunk-TQJDYHVE.js +2 -0
  112. package/out/chunk-TUII34K2.js +1 -0
  113. package/out/chunk-VDRXHIHN.js +3 -0
  114. package/out/chunk-VFWMQ3LG.js +2 -0
  115. package/out/chunk-VH4XMZIW.js +2 -0
  116. package/out/chunk-WCPJDAYA.js +10 -0
  117. package/out/chunk-WFYLMF6Z.js +10 -0
  118. package/out/chunk-WRAJVD35.js +1 -0
  119. package/out/chunk-WYLQWYLA.js +1 -0
  120. package/out/client-managed-import.js +4 -0
  121. package/out/client-managed.js +33 -0
  122. package/out/client-sync.js +11 -17
  123. package/out/compare.js +35 -0
  124. package/out/dialogs.js +55 -0
  125. package/out/github-issue-authorized.html +3 -0
  126. package/out/github-issue-authorized.js +1 -0
  127. package/out/graph.js +17 -4
  128. package/out/index.html +90 -8
  129. package/out/prism.js +14 -10
  130. package/out/scan.js +2 -0
  131. package/out/stasis.svg +20 -0
  132. package/out/terminal.js +366 -39
  133. package/out/view.css +1 -1
  134. package/out/view.js +19 -144
  135. package/package.json +364 -59
  136. package/risk/actions/findings.js +161 -0
  137. package/risk/actions/resolve.js +219 -0
  138. package/risk/actions/scripts/commands.js +182 -0
  139. package/risk/actions/scripts/grade.js +113 -0
  140. package/risk/actions/scripts/packages.js +123 -0
  141. package/risk/actions/scripts/shell.js +169 -0
  142. package/risk/actions/uses.js +76 -0
  143. package/risk/actions/workflow.js +110 -0
  144. package/risk/files/characters.js +196 -0
  145. package/risk/files/hosts.js +205 -0
  146. package/risk/files/readability.js +177 -0
  147. package/risk/files/runs-code.js +615 -0
  148. package/risk/files/script.js +118 -0
  149. package/risk/files/stamps.js +47 -0
  150. package/risk/index.d.ts +254 -0
  151. package/risk/index.js +11 -0
  152. package/risk/npm/assess.js +285 -0
  153. package/risk/npm/combined.js +68 -0
  154. package/risk/npm/compare.js +257 -0
  155. package/risk/npm/dependencies.js +172 -0
  156. package/risk/npm/diff.js +79 -0
  157. package/risk/npm/downloads.js +84 -0
  158. package/risk/npm/files.js +445 -0
  159. package/risk/npm/held.js +63 -0
  160. package/risk/npm/install-scripts.js +34 -0
  161. package/risk/npm/malware.js +36 -0
  162. package/risk/npm/manifest.js +65 -0
  163. package/risk/npm/new-dependency.js +60 -0
  164. package/risk/npm/old-version.js +108 -0
  165. package/risk/npm/registry.js +388 -0
  166. package/risk/npm/release.js +85 -0
  167. package/risk/npm/score.js +59 -0
  168. package/risk/npm/specs.js +124 -0
  169. package/risk/npm/tarball.js +96 -0
  170. package/server-common/database-config.ts +16 -0
  171. package/server-common/initialize.ts +18 -0
  172. package/server-common/npm-advisories.ts +101 -0
  173. package/{server → server-common}/origin.ts +5 -5
  174. package/server-common/reap.ts +48 -0
  175. package/server-common/scan-config.ts +19 -0
  176. package/server-common/standalone.ts +29 -0
  177. package/server-common/storage-crypto.ts +182 -0
  178. package/server-common/storage-log.ts +38 -0
  179. package/server-common/vercel-blob.ts +103 -0
  180. package/server-e2e/app.ts +487 -0
  181. package/{server → server-e2e}/bus-receiver.ts +2 -1
  182. package/{server → server-e2e}/cli.js +9 -4
  183. package/{server → server-e2e}/config.ts +50 -42
  184. package/{server → server-e2e}/db-neon.ts +2 -2
  185. package/{server → server-e2e}/db-revision-sql.ts +1 -1
  186. package/{server → server-e2e}/db-stmt.ts +2 -2
  187. package/{server → server-e2e}/db.ts +7 -7
  188. package/{server → server-e2e}/http.ts +44 -13
  189. package/{server → server-e2e}/hub.ts +40 -9
  190. package/server-e2e/index.ts +17 -0
  191. package/server-e2e/lifecycle.ts +95 -0
  192. package/{server → server-e2e}/neon-driver.ts +2 -2
  193. package/{server → server-e2e}/npm-proxy.ts +12 -145
  194. package/{server → server-e2e}/objstore/blob-vercel.ts +10 -114
  195. package/{server → server-e2e}/objstore/blob.ts +1 -1
  196. package/server-e2e/objstore/fetch-mint-guard.ts +74 -0
  197. package/{server → server-e2e}/objstore/handlers.ts +9 -8
  198. package/{server → server-e2e}/objstore/init.ts +19 -27
  199. package/{server → server-e2e}/objstore/reaper.ts +1 -1
  200. package/server-e2e/objstore/rest-deny.ts +28 -0
  201. package/server-e2e/objstore/rest-mint.ts +224 -0
  202. package/{server → server-e2e}/objstore/rest.ts +3 -3
  203. package/{server → server-e2e}/objstore/store-neon.ts +6 -10
  204. package/{server → server-e2e}/objstore/store.ts +7 -7
  205. package/{server → server-e2e}/objstore/tokens.ts +2 -2
  206. package/{server → server-e2e}/pubsub.ts +12 -9
  207. package/{server → server-e2e}/sign.ts +4 -4
  208. package/{server → server-e2e}/sse-server.ts +60 -33
  209. package/{server → server-e2e}/static.ts +31 -21
  210. package/{server → server-e2e}/sync-handlers.ts +64 -30
  211. package/{server → server-e2e}/ws-server.ts +20 -9
  212. package/server-managed/activity.ts +258 -0
  213. package/server-managed/agent-merges.ts +109 -0
  214. package/server-managed/app-agents.ts +23 -0
  215. package/server-managed/avatar-store.ts +13 -0
  216. package/server-managed/blob-store.ts +17 -0
  217. package/server-managed/brotli.ts +10 -0
  218. package/server-managed/bundle-advisories.ts +120 -0
  219. package/server-managed/bundle-advisory-inventory.ts +77 -0
  220. package/server-managed/bundle-build-diagnostics.d.ts +10 -0
  221. package/server-managed/bundle-build-diagnostics.js +23 -0
  222. package/server-managed/bundle-build-leases.ts +41 -0
  223. package/server-managed/bundle-build-worker.js +115 -0
  224. package/server-managed/bundle-build.ts +200 -0
  225. package/server-managed/bundle-cache.ts +211 -0
  226. package/server-managed/bundle-catalog.ts +29 -0
  227. package/server-managed/bundle-commits.ts +140 -0
  228. package/server-managed/bundle-store.ts +24 -0
  229. package/server-managed/bundle-summary-cache.ts +130 -0
  230. package/server-managed/bundle.ts +119 -0
  231. package/server-managed/cache-storage.ts +20 -0
  232. package/server-managed/cli.js +21 -0
  233. package/server-managed/combined.ts +47 -0
  234. package/server-managed/comments.ts +151 -0
  235. package/server-managed/commit-coauthors.ts +106 -0
  236. package/server-managed/commit-lockfiles.ts +76 -0
  237. package/server-managed/comparison.ts +52 -0
  238. package/server-managed/config.ts +164 -0
  239. package/server-managed/content-access.ts +15 -0
  240. package/server-managed/crypto.ts +25 -0
  241. package/server-managed/db-methods.ts +1923 -0
  242. package/server-managed/db-neon.ts +468 -0
  243. package/server-managed/db-schema.ts +238 -0
  244. package/server-managed/db-table-names.ts +22 -0
  245. package/server-managed/db.ts +143 -0
  246. package/server-managed/dependabot-access.ts +60 -0
  247. package/server-managed/dependabot-alerts.ts +185 -0
  248. package/server-managed/dependency-review-comments.ts +98 -0
  249. package/server-managed/dependency-reviews.ts +364 -0
  250. package/server-managed/github-accounts.ts +60 -0
  251. package/server-managed/github-actions.ts +71 -0
  252. package/server-managed/github-app.ts +376 -0
  253. package/server-managed/github-errors.ts +70 -0
  254. package/server-managed/github-graphql.ts +39 -0
  255. package/server-managed/github-issue-links.ts +140 -0
  256. package/server-managed/github-issue-oauth.ts +47 -0
  257. package/server-managed/github-issues.ts +152 -0
  258. package/server-managed/github-link-oauth.ts +69 -0
  259. package/server-managed/github-links.ts +204 -0
  260. package/server-managed/github-metadata.ts +213 -0
  261. package/server-managed/github-oauth.ts +238 -0
  262. package/server-managed/github-pulls.ts +227 -0
  263. package/server-managed/github-requests.ts +136 -0
  264. package/server-managed/github-users.ts +30 -0
  265. package/server-managed/http-response.ts +76 -0
  266. package/server-managed/http.ts +5503 -0
  267. package/server-managed/import-triage.ts +51 -0
  268. package/server-managed/index.ts +192 -0
  269. package/server-managed/kept-briefly.ts +37 -0
  270. package/server-managed/kept.ts +28 -0
  271. package/server-managed/link-reports.ts +162 -0
  272. package/server-managed/listed-activity.ts +39 -0
  273. package/server-managed/locked-packages.ts +152 -0
  274. package/server-managed/lockfile-scope.ts +49 -0
  275. package/server-managed/lru.ts +30 -0
  276. package/server-managed/managed-issues.ts +78 -0
  277. package/server-managed/management-catalog.ts +45 -0
  278. package/server-managed/management.ts +99 -0
  279. package/server-managed/npm-delta-worker.js +23 -0
  280. package/server-managed/npm-delta.ts +194 -0
  281. package/server-managed/npm-insights.ts +449 -0
  282. package/server-managed/npm-kept.ts +225 -0
  283. package/server-managed/npm-loads.ts +264 -0
  284. package/server-managed/npm-manifest.ts +115 -0
  285. package/server-managed/npm-packages.ts +454 -0
  286. package/server-managed/npm-patch.ts +325 -0
  287. package/server-managed/npm-provenance.ts +123 -0
  288. package/server-managed/npm-risk.ts +285 -0
  289. package/server-managed/object-storage-disk.ts +148 -0
  290. package/server-managed/object-storage-vercel.ts +67 -0
  291. package/server-managed/object-storage.ts +75 -0
  292. package/server-managed/package-entry-points.ts +51 -0
  293. package/server-managed/pretty-print.ts +434 -0
  294. package/server-managed/public-workspace.ts +238 -0
  295. package/server-managed/pull-comment-reviews.ts +161 -0
  296. package/server-managed/pull-dependencies.ts +351 -0
  297. package/server-managed/pull-dependency-reviews.ts +334 -0
  298. package/server-managed/pull-reviews.ts +205 -0
  299. package/server-managed/queue.ts +39 -0
  300. package/server-managed/release-commits.ts +84 -0
  301. package/server-managed/rename-diff-worker.js +26 -0
  302. package/server-managed/rename-diff.ts +279 -0
  303. package/server-managed/repo-path.ts +21 -0
  304. package/server-managed/report-migration.ts +35 -0
  305. package/server-managed/report-query.ts +10 -0
  306. package/server-managed/report-response.ts +16 -0
  307. package/server-managed/report-sources.ts +178 -0
  308. package/server-managed/report-store.ts +85 -0
  309. package/server-managed/repository-actions.ts +218 -0
  310. package/server-managed/repository-agents.ts +448 -0
  311. package/server-managed/repository-aliases.ts +91 -0
  312. package/server-managed/repository-browser.ts +294 -0
  313. package/server-managed/repository-changes.ts +284 -0
  314. package/server-managed/repository-code.ts +215 -0
  315. package/server-managed/repository-dependency-reviews.ts +166 -0
  316. package/server-managed/repository-discovery.ts +82 -0
  317. package/server-managed/repository-history.ts +432 -0
  318. package/server-managed/repository-issues.ts +195 -0
  319. package/server-managed/repository-policy.ts +27 -0
  320. package/server-managed/repository-pulls.ts +212 -0
  321. package/server-managed/repository-pulse.ts +460 -0
  322. package/server-managed/repository-risk-assess.ts +105 -0
  323. package/server-managed/repository-risk-audit.ts +79 -0
  324. package/server-managed/repository-risk-read.ts +170 -0
  325. package/server-managed/repository-risk.ts +491 -0
  326. package/server-managed/repository-squashes.ts +168 -0
  327. package/server-managed/request-timing.ts +63 -0
  328. package/server-managed/reviewed-dependencies.ts +149 -0
  329. package/server-managed/revisions.ts +35 -0
  330. package/server-managed/scan-downloads.ts +87 -0
  331. package/server-managed/session.ts +132 -0
  332. package/server-managed/slugs.ts +38 -0
  333. package/server-managed/solidity-entry-points.ts +71 -0
  334. package/server-managed/sql-postgres.ts +34 -0
  335. package/server-managed/sql.ts +88 -0
  336. package/server-managed/static.ts +28 -0
  337. package/server-managed/storage-command.ts +16 -0
  338. package/server-managed/storage-db.ts +183 -0
  339. package/server-managed/storage-encryption.ts +290 -0
  340. package/server-managed/storage-maintenance.ts +259 -0
  341. package/server-managed/storage-payload.ts +121 -0
  342. package/server-managed/storage-stores.ts +91 -0
  343. package/server-managed/storage.ts +82 -0
  344. package/server-managed/team-app.ts +134 -0
  345. package/server-managed/team-catalog.ts +7 -0
  346. package/server-managed/team-feed.ts +180 -0
  347. package/server-managed/team-npm-scopes.ts +106 -0
  348. package/server-managed/team-reports.ts +319 -0
  349. package/server-managed/triage-response.ts +16 -0
  350. package/server-managed/type-only-worker.js +19 -0
  351. package/server-managed/type-only.ts +105 -0
  352. package/server-managed/uploads.ts +106 -0
  353. package/server-managed/upstream-cache.ts +243 -0
  354. package/server-managed/view-as.ts +58 -0
  355. package/server-managed/workspace-shares.ts +169 -0
  356. package/server.ts +70 -0
  357. package/server/index.ts +0 -480
  358. package/server/lifecycle.ts +0 -208
  359. /package/{LICENSE → LICENSE-MIT} +0 -0
  360. /package/{server → server-e2e}/auth.ts +0 -0
  361. /package/{server → server-e2e}/config.example.json +0 -0
  362. /package/{server → server-e2e}/objstore/blob-fs.ts +0 -0
  363. /package/{server → server-e2e}/objstore/fs.ts +0 -0
  364. /package/{server → server-e2e}/objstore/sign.ts +0 -0
  365. /package/{server → server-e2e}/peer.ts +0 -0
  366. /package/{server → server-e2e}/sse-session.ts +0 -0
  367. /package/{server → server-e2e}/util.ts +0 -0
  368. /package/{server → server-e2e}/validation.ts +0 -0
@@ -0,0 +1,172 @@
1
+ // What calls for a look among the dependencies a package version names: one
2
+ // npm fetches from elsewhere than its registry, and one nothing of the
3
+ // version imports, which npm installs all the same, running its install
4
+ // scripts, as a dependency added to run them alone would be.
5
+ import { isNpmPackageName } from '../../common/managed/npm-packages.js'
6
+ import { NPM_PROSE } from '../files/readability.js'
7
+ import { npmAlias, npmDependencySource, npmRegistrySpec } from './specs.js'
8
+ import { npmInstallScripts } from './install-scripts.js'
9
+
10
+ // The kinds of dependency a manifest names, by its field (npmDependencies).
11
+ const NPM_DEPENDENCY_KINDS = [['dependencies', null], ['peerDependencies', 'peer'], ['optionalDependencies', 'optional']]
12
+
13
+ // A version's dependencies, each `{ name, range, kind, opens }`, in name
14
+ // order: `kind` null for a plain dependency, else 'peer' or 'optional';
15
+ // `opens` the package the viewer opens for it, the one an `npm:` alias
16
+ // names, or null when it names none, or where npm doesn't install it from
17
+ // its registry (specs.js npmRegistrySpec: a path, a repository, a URL),
18
+ // whose package of that name isn't what it installs.
19
+ export function npmDependencies(manifest) {
20
+ const rows = []
21
+ for (const [field, kind] of NPM_DEPENDENCY_KINDS) {
22
+ for (const [name, range] of Object.entries(manifest?.[field] ?? {})) {
23
+ const opens = npmAlias(range)?.name ?? name
24
+ rows.push({ name, range, kind, opens: isNpmPackageName(opens) && npmRegistrySpec(range) ? opens : null })
25
+ }
26
+ }
27
+ return rows.toSorted((a, b) => a.name.localeCompare(b.name) || (a.kind ?? '').localeCompare(b.kind ?? ''))
28
+ }
29
+
30
+ // What two versions' dependencies differ in, by name and kind: those only
31
+ // `base` has, those only `other` has, and those whose range changed.
32
+ export function npmDependencyChanges(base, other) {
33
+ const index = manifest => new Map(npmDependencies(manifest).map(row => [`${row.kind ?? ''}\0${row.name}`, row]))
34
+ const after = index(other), before = index(base)
35
+ const added = [], changed = [], removed = []
36
+ for (const [key, { name, kind, range, opens }] of before) {
37
+ const next = after.get(key)
38
+ if (!next) removed.push({ key, name, kind, range, opens })
39
+ else if (next.range !== range) changed.push({ key, name, kind, from: range, to: next.range, opens: next.opens })
40
+ }
41
+ for (const [key, { name, kind, range, opens }] of after) if (!before.has(key)) added.push({ key, name, kind, range, opens })
42
+ return { removed, added, changed }
43
+ }
44
+
45
+ // Files no import is read from: prose, as the Overview tells it
46
+ // (readability.js NPM_PROSE; not code named alike, as
47
+ // `lib/history.js`), but MDX, which imports what it renders; source maps,
48
+ // which quote whatever their sources import, and lockfiles, which name every
49
+ // package installed; nor the package's own package.json, which names them all.
50
+ const MDX = /\.mdx$/iu
51
+ const NO_IMPORTS = /(?:\.map|(?:^|\/)(?:package-lock\.json|npm-shrinkwrap\.json|yarn\.lock|pnpm-lock\.yaml))$/iu
52
+ // A quote, then a run of the characters names and paths are made of (after
53
+ // webpack's `~` too), and what ends it: a quote, escaped too, as a quote in
54
+ // a quoted command is in binding.gyp (`require(\'nan\')`), or a template's `${`.
55
+ const QUOTED = /["'`]~?([\w@./-]+)(\\?["'`]|\$\{)?/gu
56
+
57
+ // What of a version names a package as an import would, once a version, in
58
+ // one pass over its code files however many names are asked about: `whole`,
59
+ // each name quoted alone or before a path in it (`'name'`, `"name/sub"`);
60
+ // `parts`, each part of a name up to a `-` or `/` that a quote or a
61
+ // template's `${` ends, as a package builds a name from one to pick the
62
+ // build for the platform it runs on (`` `@rollup/rollup-${platform}` ``,
63
+ // `"@typescript/" + name`); and `commands`, the install scripts npm runs.
64
+ const importsByVersion = new WeakMap()
65
+ function importsOf({ json, npm }) {
66
+ let found = importsByVersion.get(json)
67
+ if (found) return found
68
+ const sources = json.sources ?? [], texts = json.sourcesContent ?? []
69
+ found = { whole: new Set(), parts: new Set(), commands: npmInstallScripts(npm?.manifest, sources).map(([, command]) => command) }
70
+ for (const [i, path] of sources.entries()) {
71
+ if (typeof texts[i] !== 'string' || path === 'package.json' || (NPM_PROSE.test(path) && !MDX.test(path)) || NO_IMPORTS.test(path)) continue
72
+ for (const [, run, end] of texts[i].matchAll(QUOTED)) {
73
+ if (end && '-/'.includes(run.at(-1))) found.parts.add(run)
74
+ if (end && end !== '${') found.whole.add(run)
75
+ for (let at = run.indexOf('/'); at !== -1; at = run.indexOf('/', at + 1)) found.whole.add(run.slice(0, at))
76
+ }
77
+ }
78
+ importsByVersion.set(json, found)
79
+ return found
80
+ }
81
+
82
+ // Whether a command names `name`: as a word (`prebuild-install || node-gyp
83
+ // rebuild`), or as the package installed in `node_modules` that a path
84
+ // leads into (`node node_modules/name/setup.js`); not as part of another
85
+ // path (`node scripts/name.js`, `node_modules/@scope/name`).
86
+ function commandNames(command, name) {
87
+ for (let at = command.indexOf(name); at !== -1; at = command.indexOf(name, at + 1)) {
88
+ const before = command.slice(0, at)
89
+ if ((!/[\w@./-]$/u.test(before) || /node_modules\/(?:\.bin\/)?$/u.test(before)) && !/^[\w.-]/u.test(command.slice(at + name.length))) return true
90
+ }
91
+ return false
92
+ }
93
+
94
+ // DefinitelyTyped's stub of a package's types, once the package has types
95
+ // of its own (`@types/mkdirp` 2.0.0), depending on that package, the
96
+ // registry's by its name (`spec`, no `npm:` alias of another, no repository,
97
+ // URL or path): named as DefinitelyTyped publishes the package's types
98
+ // (`@types/name`, a scoped one's `@types/scope__name`), with nothing but
99
+ // prose beside its package.json, no declarations of its own.
100
+ const typesOf = name => `@types/${name.startsWith('@') ? name.slice(1).replace('/', '__') : name}`
101
+ const typesStubOf = ({ json, npm }, name, spec) => npm?.name === typesOf(name) && npmRegistrySpec(spec) && (npmAlias(spec)?.name ?? name) === name
102
+ && (json.sources ?? []).every(path => path === 'package.json' || (NPM_PROSE.test(path) && !MDX.test(path)))
103
+
104
+ // Whether nothing of a version (`details`, its files and manifest) imports
105
+ // a dependency it names (`{ name, kind, range }`, or a changed one's `to`,
106
+ // npm-package.js npmDependencies): no code file names it as an import
107
+ // would, whole or by a part of it, nor does an install script npm runs
108
+ // (importsOf); never where its files aren't read. Not a peer dependency,
109
+ // which the package expects whoever installs it to provide, nor types from
110
+ // DefinitelyTyped (`@types/`), imported by another name, where no one else
111
+ // publishes; nor tslib, TypeScript's helpers, which a package compiled
112
+ // with them inlined names all the same, as a third of popular packages
113
+ // naming a dependency nothing imports do; each where npm installs that
114
+ // package from its registry (installsAs), not another under its name
115
+ // (`"tslib": "npm:other@1"`, a repository); nor the package a stub of
116
+ // DefinitelyTyped's types of it depends on (typesStubOf), with no code to
117
+ // import it.
118
+ const installsAs = (name, spec) => npmRegistrySpec(spec) ? npmAlias(spec)?.name ?? name : null
119
+ export function npmDependencyUnimported(details, { name, kind, range, to }) {
120
+ const installs = installsAs(name, to ?? range)
121
+ if (!details?.json || kind === 'peer' || (name.startsWith('@types/') && installs?.startsWith('@types/')) || (name === 'tslib' && installs === 'tslib')
122
+ || typesStubOf(details, name, to ?? range)) return false
123
+ const { whole, parts, commands } = importsOf(details)
124
+ return !whole.has(name) && ![...name.slice(1).matchAll(/[-/]/gu)].some(({ index }) => parts.has(name.slice(0, index + 2)))
125
+ && !commands.some(command => commandNames(command, name))
126
+ }
127
+
128
+ // Whether a dependency nothing of a version imports (`{ name, range }`,
129
+ // npmDependencyUnimported) counts toward its risk (assess.js): not lodash,
130
+ // which packages keep naming long after their code stopped importing it, nor
131
+ // Babel's own presets (`@babel/preset-*`), which a config names by a string
132
+ // (an ESLint config's parser options); each where npm installs that package
133
+ // from its registry (installsAs), not another under its name.
134
+ export function npmUnusedDependencyCounted({ name, range }) {
135
+ const installs = installsAs(name, range)
136
+ return !(name === 'lodash' && installs === 'lodash') && !(name.startsWith('@babel/preset-') && installs?.startsWith('@babel/preset-'))
137
+ }
138
+
139
+ // Where a source takes a dependency from, in a sentence.
140
+ const SOURCE_NAMES = new Map([['URL', 'a URL'], ['git', 'a git repository']])
141
+
142
+ // What calls for a look at a dependency a version (`details`) names
143
+ // (`{ name, kind, range }`, or a changed one's `to`), as tags
144
+ // (`{ check, label, tooltip }`): `source` where npm fetches it from
145
+ // elsewhere than its registry (npmDependencySource), its spec in the
146
+ // tooltip, and `unimported` where nothing of the version imports it
147
+ // (npmDependencyUnimported). Against the specs another version named its
148
+ // dependencies by (`was`, npmDependencySpecs), only where it named this one
149
+ // by another spec, or, imported by nothing, not at all.
150
+ export function npmDependencyFlags(details, row, was = null) {
151
+ const spec = row.to ?? row.range
152
+ const source = npmDependencySource(spec)
153
+ return [
154
+ ...source && was?.get(row.name) !== spec ? [{ check: 'source', label: 'non-registry', tooltip: `From ${SOURCE_NAMES.get(source) ?? source}, not npm's registry: ${spec}` }] : [],
155
+ ...!was?.has(row.name) && npmDependencyUnimported(details, row) ? [{ check: 'unimported', label: 'unimported',
156
+ tooltip: `${was ? `New in ${details.npm?.version}, and nothing of it` : `Nothing of ${details.npm?.version}`} imports it: no code file names it as an import would, nor does an install script` }] : [],
157
+ ]
158
+ }
159
+
160
+ // The specs a manifest names its dependencies by, by name, whatever their
161
+ // kind.
162
+ export const npmDependencySpecs = manifest => new Map(NPM_DEPENDENCY_KINDS.flatMap(([field]) => Object.entries(manifest?.[field] ?? {})))
163
+
164
+ // Two versions' dependency changes (npm-package.js npmDependencyChanges),
165
+ // each the open version (`other`) adds or changes with its `flags`
166
+ // (npmDependencyFlags) against the one compared with (`base`), or alone,
167
+ // none given.
168
+ export function npmFlagDependencyChanges({ removed, added, changed }, base, other) {
169
+ const was = base ? npmDependencySpecs(base.npm?.manifest) : null
170
+ const flag = row => ({ ...row, flags: npmDependencyFlags(other, row, was) })
171
+ return { removed, added: added.map(flag), changed: changed.map(flag) }
172
+ }
@@ -0,0 +1,79 @@
1
+ // How two versions' files differ, as Compare's diff has them
2
+ // (ui/view/bundle-compare-diff.js computeBundleDiff), for risk's findings
3
+ // (compare.js): the files the open version adds, and those it changes, a
4
+ // build's content-hashed file paired with the one it replaces by its name
5
+ // (hashedKey), and a file moved to another path with its old one by content,
6
+ // as git diff -M pairs them (@preventive/compare compareTrees).
7
+ import { compareTrees } from '@preventive/compare'
8
+ import { bundleFilesAsMap } from '../../common/bundle-sources.js'
9
+
10
+ // git's diff.renameLimit for a package's renames: past this many removed files
11
+ // by added ones, squared, no pair is scored, and only identical content pairs
12
+ // them.
13
+ const RENAME_LIMIT = 250
14
+
15
+ // Whether two sides hold the same file: text as text, one that isn't by its
16
+ // digest (bundle-compare-diff.js sameContent).
17
+ const same = (a, b) => a === b || (a?.format === 'digest' && b?.format === 'digest' && a.digest === b.digest)
18
+
19
+ // A file's content as compareTrees reads it to find renames: its text, its
20
+ // digest for one that isn't, or else its place in its list, so that only its
21
+ // path pairs it (bundle-compare-diff.js treeData).
22
+ const treeData = (content, at) => typeof content === 'string' ? `\0text\0${content.toWellFormed()}`
23
+ : content?.format === 'digest' ? `\0digest\0${content.digest}` : `\0none\0${at}`
24
+
25
+ // A build's content-hashed file (`assets/index-CyWAfh-p.js`, Vite's and
26
+ // Rollup's `[name]-[hash]`, webpack's `[name].[contenthash].js`) by its name
27
+ // but its hash, `assets/index\0.js`: a release renames it as its content
28
+ // changes, the same file. Null for a name whose last part before its
29
+ // extension is no hash: 8 to 32 of base64url's characters with a letter, and
30
+ // a digit or a capital, as neither a lowercase word (`index-client.js`,
31
+ // `lodash.debounce.js`) nor a number (a date, `migration-20240101.js`) is. Its hash its last 8 or more such characters, so that chunks named
32
+ // after a module (`vendor-react-DQ4mN2sx.js`) pair each with its own; a
33
+ // longer hash holding a `-` (Rollup's `[hash:16]`) is cut there, so that a
34
+ // rebuild pairs by its content alone, as its parts aren't told from a
35
+ // chunk's name's (`vendor-ui-…`) without it.
36
+ const HASHED_NAME = /^(.+)[-.]([\w-]{8,32})((?:\.[A-Za-z\d]+)+)$/u
37
+ function hashedKey(path) {
38
+ const at = path.lastIndexOf('/') + 1
39
+ const [, stem, hash, extension] = HASHED_NAME.exec(path.slice(at)) ?? []
40
+ if (!hash || !/[A-Za-z]/u.test(hash) || !/[\dA-Z]/u.test(hash)) return null
41
+ return `${path.slice(0, at)}${stem}\0${extension}`
42
+ }
43
+
44
+ // `onlyOther`, the files the open version (`other`) adds, `{ path }` each, and
45
+ // `changed`, those it changes, `{ path, modified }`, or moves, `{ path,
46
+ // renamedFrom, modified }`, `modified` whether its content changed too; each
47
+ // version as compare.js takes one. A file that isn't text is
48
+ // told by its digest (npmBinaries), as the viewer's Compare tells it.
49
+ export function npmFilesDiff(base, other) {
50
+ const after = bundleFilesAsMap(other), before = bundleFilesAsMap(base)
51
+ const changed = [], onlyBase = [], onlyOther = []
52
+ for (const [path, text] of after) {
53
+ if (!before.has(path)) onlyOther.push(path)
54
+ else if (!same(before.get(path), text)) changed.push({ path, modified: true })
55
+ }
56
+ for (const path of before.keys()) if (!after.has(path)) onlyBase.push(path)
57
+ // A content-hashed file added where one by the same name but its hash
58
+ // (hashedKey) is removed, one each, is that file changed, as a build
59
+ // renames it: what it runs, how it reads and how large it is told against
60
+ // it, not all of it new.
61
+ const hashed = paths => Map.groupBy(paths.filter(hashedKey), hashedKey)
62
+ const moved = new Set(), removed = hashed(onlyBase)
63
+ for (const [key, paths] of hashed(onlyOther)) {
64
+ const was = removed.get(key)
65
+ if (paths.length !== 1 || was?.length !== 1) continue
66
+ changed.push({ path: paths[0], renamedFrom: was[0], modified: !same(before.get(was[0]), after.get(paths[0])) })
67
+ moved.add(paths[0])
68
+ }
69
+ // The rest by content, against every file removed: one paired by its name
70
+ // may be another's too, a build's copy of it elsewhere.
71
+ const entries = (paths, files, side) => paths.filter(path => path.isWellFormed()).map((path, at) => ({ name: path, data: treeData(files.get(path), `${side}${at}`) }))
72
+ const renames = compareTrees(entries(onlyBase, before, 'a'), entries(onlyOther.filter(path => !moved.has(path)), after, 'b'), { paths: 'keys', renameLimit: RENAME_LIMIT, lines: false })
73
+ .filter(change => change.status === 'renamed')
74
+ for (const change of renames) {
75
+ changed.push({ path: change.new.path, renamedFrom: change.old.path, modified: change.similarity !== 100 })
76
+ moved.add(change.new.path)
77
+ }
78
+ return { onlyOther: onlyOther.filter(path => !moved.has(path)).map(path => ({ path })), changed }
79
+ }
@@ -0,0 +1,84 @@
1
+ // A package's downloads, as npm's downloads API counts them a day
2
+ // (`{ start, end, days }`, the first day's date and a count a day from it):
3
+ // in weeks and months, the tier among npm's packages they put it in, and a
4
+ // dependency's to the day before its latest two weeks.
5
+
6
+ export const DAY_MS = 24 * 60 * 60_000
7
+ // The date `offset` days past a count's first day (`start`).
8
+ export const dayOf = (start, offset) => new Date(Date.parse(start) + offset * DAY_MS)
9
+
10
+ // The downloads in 7-day weeks ending on the last day, oldest first, a
11
+ // partial oldest week left out: `{ from, to, total }`, dates as `Date`s.
12
+ export function npmDownloadWeeks(downloads) {
13
+ const weeks = []
14
+ if (!downloads?.days) return weeks
15
+ for (let end = downloads.days.length - 1; end >= 6; end -= 7) {
16
+ let total = 0
17
+ for (let day = end - 6; day <= end; day++) total += downloads.days[day]
18
+ weeks.push({ from: dayOf(downloads.start, end - 6), to: dayOf(downloads.start, end), total })
19
+ }
20
+ return weeks.toReversed()
21
+ }
22
+
23
+ // The downloads in calendar months (UTC), oldest first, the partial months
24
+ // at either end left out: `{ from, to, total }`, as npmDownloadWeeks has them.
25
+ export function npmDownloadMonths(downloads) {
26
+ const months = []
27
+ if (!downloads?.days) return months
28
+ const start = Date.parse(downloads.start)
29
+ let month = null
30
+ downloads.days.forEach((count, i) => {
31
+ const day = new Date(start + i * DAY_MS)
32
+ if (day.getUTCDate() === 1) {
33
+ month = { from: day, to: day, total: 0 }
34
+ months.push(month)
35
+ }
36
+ if (month) { month.total += count; month.to = day }
37
+ })
38
+ const last = months.at(-1)
39
+ if (last && new Date(last.to.getTime() + DAY_MS).getUTCDate() !== 1) months.pop()
40
+ return months
41
+ }
42
+
43
+ // The weekly downloads from which a package is among npm's top 10, 20, 50,
44
+ // 100, …, 100,000 packages, rounded: download-counts' snapshot of a month's
45
+ // downloads (Jan 30 – Feb 28, 2026), each rank's count grown by the median
46
+ // growth of the packages ranked around it to the 30 days Sep 9 – Oct 8,
47
+ // 2026, as npm counts them, days it failed to count among them, then made a
48
+ // week. An estimate, to be revisited. Below the last, a package is
49
+ // unpopular (Infinity).
50
+ const TIERS = [
51
+ [10, 550e6], [20, 405e6], [50, 285e6], [100, 225e6], [200, 165e6], [500, 95e6], [1000, 50e6], [2000, 20e6], [5000, 4e6],
52
+ [10_000, 850e3], [20_000, 155e3], [50_000, 18e3], [100_000, 2e3], [Infinity, 0],
53
+ ]
54
+
55
+ // The tier a package's downloads put it in (TIERS), by the most it had in any
56
+ // of its latest six 7-day weeks (npmDownloadWeeks): a week lower for a day
57
+ // npm failed to count, as it now and then does, or for a holiday, is passed
58
+ // over. Infinity for an unpopular one; null without a week.
59
+ export function npmDownloadTier(downloads) {
60
+ const latest = npmDownloadWeeks(downloads).slice(-6)
61
+ if (latest.length === 0) return null
62
+ const most = Math.max(...latest.map(week => week.total))
63
+ return TIERS.find(([, from]) => most >= from)[0]
64
+ }
65
+
66
+ // A tier's name, and its tint: solid green to the top 200, green to the top
67
+ // 5,000, yellow to the top 20,000, orange to the top 100,000, and red for
68
+ // the unpopular, a package few would notice changing.
69
+ export const npmTierLevel = tier => tier <= 200 ? 'is-solid' : tier <= 5000 ? 'is-top' : tier <= 20_000 ? 'is-mid' : tier <= 100_000 ? 'is-low' : 'is-unpopular'
70
+ // A dependency's downloads to the day before its latest two weeks: a
71
+ // package a release just pulled in, as a compromised one depending on it
72
+ // would, could owe those to that release alone. Its own package's tier goes
73
+ // by all of them, its Overview charting them.
74
+ const DEPENDENCY_DAYS_LEFT_OUT = 14
75
+ export function npmSettledDownloads(downloads, now = Date.now()) {
76
+ if (!downloads) return null
77
+ const first = Math.floor(Date.parse(downloads.start) / DAY_MS), last = Math.floor(now / DAY_MS) - 1 - DEPENDENCY_DAYS_LEFT_OUT
78
+ const days = downloads.days.slice(0, Math.max(0, last - first + 1))
79
+ return { start: downloads.start, end: new Date((first + days.length - 1) * DAY_MS).toISOString().slice(0, 10), days }
80
+ }
81
+
82
+ // A tier orange or red, past the top 20,000: one few would notice changing,
83
+ // as a release past the top 20,000 is flagged (compare.js).
84
+ export const npmTierLow = tier => tier > 20_000