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