| 'use strict' | 
|   | 
| const util = require('util') | 
|   | 
| const fs = require('fs') | 
| const fsm = require('fs-minipass') | 
| const ssri = require('ssri') | 
| const contentPath = require('./path') | 
| const Pipeline = require('minipass-pipeline') | 
|   | 
| const lstat = util.promisify(fs.lstat) | 
| const readFile = util.promisify(fs.readFile) | 
|   | 
| module.exports = read | 
|   | 
| const MAX_SINGLE_READ_SIZE = 64 * 1024 * 1024 | 
| function read (cache, integrity, opts = {}) { | 
|   const { size } = opts | 
|   return withContentSri(cache, integrity, (cpath, sri) => { | 
|     // get size | 
|     return lstat(cpath).then(stat => ({ stat, cpath, sri })) | 
|   }).then(({ stat, cpath, sri }) => { | 
|     if (typeof size === 'number' && stat.size !== size) | 
|       throw sizeError(size, stat.size) | 
|   | 
|     if (stat.size > MAX_SINGLE_READ_SIZE) | 
|       return readPipeline(cpath, stat.size, sri, new Pipeline()).concat() | 
|   | 
|     return readFile(cpath, null).then((data) => { | 
|       if (!ssri.checkData(data, sri)) | 
|         throw integrityError(sri, cpath) | 
|   | 
|       return data | 
|     }) | 
|   }) | 
| } | 
|   | 
| const readPipeline = (cpath, size, sri, stream) => { | 
|   stream.push( | 
|     new fsm.ReadStream(cpath, { | 
|       size, | 
|       readSize: MAX_SINGLE_READ_SIZE, | 
|     }), | 
|     ssri.integrityStream({ | 
|       integrity: sri, | 
|       size, | 
|     }) | 
|   ) | 
|   return stream | 
| } | 
|   | 
| module.exports.sync = readSync | 
|   | 
| function readSync (cache, integrity, opts = {}) { | 
|   const { size } = opts | 
|   return withContentSriSync(cache, integrity, (cpath, sri) => { | 
|     const data = fs.readFileSync(cpath) | 
|     if (typeof size === 'number' && size !== data.length) | 
|       throw sizeError(size, data.length) | 
|   | 
|     if (ssri.checkData(data, sri)) | 
|       return data | 
|   | 
|     throw integrityError(sri, cpath) | 
|   }) | 
| } | 
|   | 
| module.exports.stream = readStream | 
| module.exports.readStream = readStream | 
|   | 
| function readStream (cache, integrity, opts = {}) { | 
|   const { size } = opts | 
|   const stream = new Pipeline() | 
|   withContentSri(cache, integrity, (cpath, sri) => { | 
|     // just lstat to ensure it exists | 
|     return lstat(cpath).then((stat) => ({ stat, cpath, sri })) | 
|   }).then(({ stat, cpath, sri }) => { | 
|     if (typeof size === 'number' && size !== stat.size) | 
|       return stream.emit('error', sizeError(size, stat.size)) | 
|   | 
|     readPipeline(cpath, stat.size, sri, stream) | 
|   }, er => stream.emit('error', er)) | 
|   | 
|   return stream | 
| } | 
|   | 
| let copyFile | 
| if (fs.copyFile) { | 
|   module.exports.copy = copy | 
|   module.exports.copy.sync = copySync | 
|   copyFile = util.promisify(fs.copyFile) | 
| } | 
|   | 
| function copy (cache, integrity, dest) { | 
|   return withContentSri(cache, integrity, (cpath, sri) => { | 
|     return copyFile(cpath, dest) | 
|   }) | 
| } | 
|   | 
| function copySync (cache, integrity, dest) { | 
|   return withContentSriSync(cache, integrity, (cpath, sri) => { | 
|     return fs.copyFileSync(cpath, dest) | 
|   }) | 
| } | 
|   | 
| module.exports.hasContent = hasContent | 
|   | 
| function hasContent (cache, integrity) { | 
|   if (!integrity) | 
|     return Promise.resolve(false) | 
|   | 
|   return withContentSri(cache, integrity, (cpath, sri) => { | 
|     return lstat(cpath).then((stat) => ({ size: stat.size, sri, stat })) | 
|   }).catch((err) => { | 
|     if (err.code === 'ENOENT') | 
|       return false | 
|   | 
|     if (err.code === 'EPERM') { | 
|       /* istanbul ignore else */ | 
|       if (process.platform !== 'win32') | 
|         throw err | 
|       else | 
|         return false | 
|     } | 
|   }) | 
| } | 
|   | 
| module.exports.hasContent.sync = hasContentSync | 
|   | 
| function hasContentSync (cache, integrity) { | 
|   if (!integrity) | 
|     return false | 
|   | 
|   return withContentSriSync(cache, integrity, (cpath, sri) => { | 
|     try { | 
|       const stat = fs.lstatSync(cpath) | 
|       return { size: stat.size, sri, stat } | 
|     } catch (err) { | 
|       if (err.code === 'ENOENT') | 
|         return false | 
|   | 
|       if (err.code === 'EPERM') { | 
|         /* istanbul ignore else */ | 
|         if (process.platform !== 'win32') | 
|           throw err | 
|         else | 
|           return false | 
|       } | 
|     } | 
|   }) | 
| } | 
|   | 
| function withContentSri (cache, integrity, fn) { | 
|   const tryFn = () => { | 
|     const sri = ssri.parse(integrity) | 
|     // If `integrity` has multiple entries, pick the first digest | 
|     // with available local data. | 
|     const algo = sri.pickAlgorithm() | 
|     const digests = sri[algo] | 
|   | 
|     if (digests.length <= 1) { | 
|       const cpath = contentPath(cache, digests[0]) | 
|       return fn(cpath, digests[0]) | 
|     } else { | 
|       // Can't use race here because a generic error can happen before | 
|       // a ENOENT error, and can happen before a valid result | 
|       return Promise | 
|         .all(digests.map((meta) => { | 
|           return withContentSri(cache, meta, fn) | 
|             .catch((err) => { | 
|               if (err.code === 'ENOENT') { | 
|                 return Object.assign( | 
|                   new Error('No matching content found for ' + sri.toString()), | 
|                   { code: 'ENOENT' } | 
|                 ) | 
|               } | 
|               return err | 
|             }) | 
|         })) | 
|         .then((results) => { | 
|           // Return the first non error if it is found | 
|           const result = results.find((r) => !(r instanceof Error)) | 
|           if (result) | 
|             return result | 
|   | 
|           // Throw the No matching content found error | 
|           const enoentError = results.find((r) => r.code === 'ENOENT') | 
|           if (enoentError) | 
|             throw enoentError | 
|   | 
|           // Throw generic error | 
|           throw results.find((r) => r instanceof Error) | 
|         }) | 
|     } | 
|   } | 
|   | 
|   return new Promise((resolve, reject) => { | 
|     try { | 
|       tryFn() | 
|         .then(resolve) | 
|         .catch(reject) | 
|     } catch (err) { | 
|       reject(err) | 
|     } | 
|   }) | 
| } | 
|   | 
| function withContentSriSync (cache, integrity, fn) { | 
|   const sri = ssri.parse(integrity) | 
|   // If `integrity` has multiple entries, pick the first digest | 
|   // with available local data. | 
|   const algo = sri.pickAlgorithm() | 
|   const digests = sri[algo] | 
|   if (digests.length <= 1) { | 
|     const cpath = contentPath(cache, digests[0]) | 
|     return fn(cpath, digests[0]) | 
|   } else { | 
|     let lastErr = null | 
|     for (const meta of digests) { | 
|       try { | 
|         return withContentSriSync(cache, meta, fn) | 
|       } catch (err) { | 
|         lastErr = err | 
|       } | 
|     } | 
|     throw lastErr | 
|   } | 
| } | 
|   | 
| function sizeError (expected, found) { | 
|   const err = new Error(`Bad data size: expected inserted data to be ${expected} bytes, but got ${found} instead`) | 
|   err.expected = expected | 
|   err.found = found | 
|   err.code = 'EBADSIZE' | 
|   return err | 
| } | 
|   | 
| function integrityError (sri, path) { | 
|   const err = new Error(`Integrity verification failed for ${sri} (${path})`) | 
|   err.code = 'EINTEGRITY' | 
|   err.sri = sri | 
|   err.path = path | 
|   return err | 
| } |