packument.js 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. 'use strict'
  2. const BB = require('bluebird')
  3. const fetch = require('npm-registry-fetch')
  4. const LRU = require('lru-cache')
  5. const optCheck = require('../../util/opt-check')
  6. // Corgis are cute. 🐕🐶
  7. const CORGI_DOC = 'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*'
  8. const JSON_DOC = 'application/json'
  9. module.exports = packument
  10. function packument (spec, opts) {
  11. opts = optCheck(opts)
  12. const registry = fetch.pickRegistry(spec, opts)
  13. const uri = registry.replace(/\/?$/, '/') + spec.escapedName
  14. return fetchPackument(uri, registry, spec, opts)
  15. }
  16. const MEMO = new LRU({
  17. length: m => m._contentLength,
  18. max: 200 * 1024 * 1024, // 200MB
  19. maxAge: 30 * 1000 // 30s
  20. })
  21. module.exports.clearMemoized = clearMemoized
  22. function clearMemoized () {
  23. MEMO.reset()
  24. }
  25. function fetchPackument (uri, registry, spec, opts) {
  26. const mem = pickMem(opts)
  27. const accept = opts.fullMetadata ? JSON_DOC : CORGI_DOC
  28. const memoKey = `${uri}~(${accept})`
  29. if (mem && !opts.preferOnline && mem.has(memoKey)) {
  30. return BB.resolve(mem.get(memoKey))
  31. }
  32. return fetch(uri, opts.concat({
  33. headers: {
  34. 'pacote-req-type': 'packument',
  35. 'pacote-pkg-id': `registry:${spec.name}`,
  36. accept
  37. },
  38. spec
  39. }, opts, {
  40. // Force integrity to null: we never check integrity hashes for manifests
  41. integrity: null
  42. })).then(res => res.json().then(packument => {
  43. packument._cached = res.headers.has('x-local-cache')
  44. packument._contentLength = +res.headers.get('content-length')
  45. // NOTE - we need to call pickMem again because proxy
  46. // objects get reused!
  47. const mem = pickMem(opts)
  48. if (mem) {
  49. mem.set(memoKey, packument)
  50. }
  51. return packument
  52. })).catch(err => {
  53. if (err.code === 'E404' && !opts.fullMetadata) {
  54. return fetchPackument(uri, registry, spec, opts.concat({
  55. fullMetadata: true
  56. }))
  57. } else {
  58. throw err
  59. }
  60. })
  61. }
  62. class ObjProxy {
  63. get (key) { return this.obj[key] }
  64. set (key, val) { this.obj[key] = val }
  65. }
  66. // This object is used synchronously and immediately, so
  67. // we can safely reuse it instead of consing up new ones
  68. const PROX = new ObjProxy()
  69. function pickMem (opts) {
  70. if (!opts || !opts.memoize) {
  71. return MEMO
  72. } else if (opts.memoize.get && opts.memoize.set) {
  73. return opts.memoize
  74. } else if (typeof opts.memoize === 'object') {
  75. PROX.obj = opts.memoize
  76. return PROX
  77. } else {
  78. return null
  79. }
  80. }