All files / lib util.js

48.75% Statements 78/160
90% Branches 9/10
62.5% Functions 5/8
48.75% Lines 78/160

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 16132x 32x 32x 32x 32x 32x 32x 32x 32x 32x 32x 32x 32x 32x 32x 32x 32x 32x 32x 32x 32x 32x 32x 32x 69x 69x 69x 69x 69x 69x 32x 37x 37x 37x 37x 37x 37x 37x 37x 32x 69x 69x 69x 69x 69x 69x 69x 69x 69x 69x 69x 69x 69x 69x 69x 68x 69x 1x   1x 1x 1x 1x 69x 68x 69x 32x                                                               32x                             32x 32x                                                                         32x 32x 27x 27x 27x 32x 81x 81x 81x  
'use strict'
 
const readline = require('readline')
 
const url = require('url')
const pDefer = require('p-defer')
const clientRequest = require('./client-request')
const { setTokens, api, getTokens, popValue } = require('./config')
const {
  formatError
} = require('../lib/ncm-style')
 
const E = console.error
 
module.exports = {
  apiRequest,
  formatAPIURL,
  graphql,
  queryReadline,
  queryReadlineHidden,
  refreshSession,
  reversedSplit
}
 
function formatAPIURL (pathname, query) {
  return api + url.format({
    pathname,
    query
  })
}
 
async function graphql (uri, query, variables) {
  const body = await apiRequest(
    'POST',
    uri,
    { query, variables }
  )
  return body.data
}
 
async function apiRequest (method, uri, reqbody) {
  let { token } = getTokens()
  let body
  while (!body && token) {
    try {
      const response = await clientRequest({
        method,
        uri,
        headers: {
          Authorization: `Bearer ${token}`,
          'Content-Type': 'application/json'
        },
        json: true,
        body: JSON.stringify(reqbody)
      })
      body = response.body
    } catch (error) {
      if (error.statusCode === 401) {
        token = await refreshSession()
      } else {
        throw error
      }
    }
  }
  return body
}
 
async function refreshSession () {
  // Just short-circut this if the token was provided via env...
  if (process.env.NCM_TOKEN) return process.env.NCM_TOKEN

  const refreshToken = popValue('refreshToken')
  if (!refreshToken || !refreshToken.trim()) {
    return
  }

  let refreshData
  try {
    const response = await clientRequest({
      method: 'GET',
      uri: formatAPIURL('/accounts/auth/refresh'),
      json: true,
      headers: {
        Authorization: `Bearer ${refreshToken}`
      }
    })
    refreshData = response.body
  } catch (err) {
    E()
    E(formatError('Failed to refresh session.', err))
    E()
    process.exitCode = 1
    return
  }

  setTokens(refreshData)
  return refreshData.session
}
 
function queryReadline (query) {
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
  })

  const deferred = pDefer()
  rl.question(query, answer => {
    deferred.resolve(answer)
    rl.close()
  })

  return deferred.promise
}
 
// For passwords
function queryReadlineHidden (query) {
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
  })

  const deferred = pDefer()
  rl.question(query, answer => {
    deferred.resolve(answer)
    rl.close()
  })

  // This is an unpleasant hack.
  // This functionality belongs in Node core.
  const _writeToOutput = rl._writeToOutput.bind(rl)
  rl._writeToOutput = function _writeHiddenToOutput (stringToWrite) {
    let newStr = ''
    // Do not interfere with a prompt.
    if (this._prompt && stringToWrite.startsWith(this._prompt)) {
      stringToWrite = stringToWrite.substring(this._prompt.length)
      newStr += this._prompt
    }
    // Iterate and reconstruct word characters (including spaces) to hide.
    for (let char of stringToWrite) {
      if (/^[\S ]+$/.test(char)) {
        char = '*'
      }
      newStr += char
    }
    stringToWrite = newStr

    _writeToOutput(stringToWrite)
  }

  return deferred.promise
}
 
// To be able to replicate regex lookbehinds in Node 8.
function reversedSplit (str, split) {
  return reverseStr(str).split(split).map(reverseStr).reverse()
}
 
function reverseStr (str) {
  return str.split('').reverse().join('')
}