import * as getType from 'cache-content-type' ; import * as assert from 'assert' ; import * as statuses from 'statuses' ; import { is as typeis } from 'type-is' ; import * as encodeUrl from 'encodeurl' ; import * as escape from 'escape-html' ; import * as only from 'only' ; import * as vary from 'vary' ; export const response = { _explicitStatus : false , _body : null , res : null , ctx : null , return { Object } api get header ( ) { return this . res . headers ; } , return { Object } api get headers ( ) { return this . header ; } , return { Mixed } api get body ( ) { return this . _body ; } , param { String | Buffer | Object | Stream } val api set body ( val ) { this . _body = val ; if ( null == val ) { if ( ! statuses . empty [ this . status ] ) this . status = 204 ; this . remove ( 'Content-Type' ) ; this . remove ( 'Content-Length' ) ; this . remove ( 'Transfer-Encoding' ) ; return ; } if ( ! this . _explicitStatus ) this . status = 200 ; const setType = ! this . has ( 'Content-Type' ) ; if ( 'string' === typeof val ) { if ( setType ) this . type = /^\s*</ . test ( val ) ? 'html' : 'text' ; this . length = Buffer . byteLength ( val ) ; return ; } if ( Buffer . isBuffer ( val ) ) { if ( setType ) this . type = 'bin' ; this . length = val . length ; return ; } if ( 'function' === typeof val . pipe ) { throw new Error ( 'unsupport pipe value' ) ; } this . remove ( 'Content-Length' ) ; this . type = 'json' ; } , param { String } etag api set etag ( val ) { if ( ! /^(W\/)?"/ . test ( val ) ) val = `"${ val }"` ; this . set ( 'ETag' , val ) ; } , return { String } api get etag ( ) { return this . get ( 'ETag' ) ; } , param { String } field return { boolean } api has ( field ) { return this . res . hasHeader ( field ) ; } , param { String | Object | Array } field param { String } val api set ( field , val ? ) { if ( val !== undefined ) { if ( Array . isArray ( val ) ) val = val . map ( v => ( typeof v === 'string' ? v : String ( v ) ) ) ; else if ( typeof val !== 'string' ) val = String ( val ) ; this . res . setHeader ( field , val ) ; } else if ( typeof field !== 'string' ) { for ( const key in field ) { this . set ( key , field [ key ] ) ; } } } , param { String } field param { String | Array } val api append ( field , val ) { const prev = this . get ( field ) ; if ( prev ) { val = Array . isArray ( prev ) ? prev . concat ( val ) : [ prev ] . concat ( val ) ; } return this . set ( field , val ) ; } , param { String } field return { String } api get ( field ) { return this . res . getHeader ( field ) ; } , set type ( type ) { type = getType ( type ) ; if ( type ) { this . set ( 'Content-Type' , type ) ; } else { this . remove ( 'Content-Type' ) ; } } , get type ( ) { const type = this . get ( 'Content-Type' ) ; if ( ! type ) { return '' ; } return type . split ( ';' , 1 ) [ 0 ] ; } , remove ( field ) { this . res . removeHeader ( field ) ; } , get status ( ) { return this . res . statusCode ; } , param { Number } code api set status ( code ) { assert ( Number . isInteger ( code ) , 'status code must be a number' ) ; assert ( code >= 100 && code <= 999 , `invalid status code: ${ code }` ) ; this . _explicitStatus = true ; this . res . statusCode = code ; if ( this . body && statuses . empty [ code ] ) this . body = null ; } , param { Number } n api set length ( n ) { this . set ( 'Content-Length' , String ( n ) ) ; } , return { Number } api get length ( ) { if ( this . has ( 'Content-Length' ) ) { return parseInt ( this . get ( 'Content-Length' ) , 10 ) || 0 ; } const { body } = this ; if ( ! body ) return undefined ; if ( 'string' === typeof body ) return Buffer . byteLength ( body ) ; if ( Buffer . isBuffer ( body ) ) return body . length ; return Buffer . byteLength ( JSON . stringify ( body ) ) ; } , param { String | Date } type api set lastModified ( val ) { if ( 'string' === typeof val ) val = new Date ( val ) ; this . set ( 'Last-Modified' , val . toUTCString ( ) ) ; } , return { Date } api get lastModified ( ) : Date | undefined { const date = this . get ( 'last-modified' ) ; if ( date && typeof date === 'string' ) { return new Date ( date ) ; } return undefined ; } , param { String | String [ ] } type param { String [ ] } types return { String | false } api is ( type , ... types ) { return typeis ( this . type , type , ... types ) ; } , return { Boolean } api get headerSent ( ) { return this . res . headersSent ; } , param { String } field api vary ( field ) { if ( this . headerSent ) return ; vary ( this . res , field ) ; } , param { String } url param { String } alt api redirect ( url , alt ? ) { if ( 'back' === url ) url = this . ctx . get ( 'Referrer' ) || alt || '/' ; this . set ( 'Location' , encodeUrl ( url ) ) ; if ( ! statuses . redirect [ this . status ] ) this . status = 302 ; if ( this . ctx . accepts ( 'html' ) ) { url = escape ( url ) ; this . type = 'text/html; charset=utf-8' ; this . body = `Redirecting to <a href="${ url }">${ url }</a>.` ; return ; } this . type = 'text/plain; charset=utf-8' ; this . body = `Redirecting to ${ url }.` ; } , return { Object } api inspect ( ) { if ( ! this . res ) return ; const o = this . toJSON ( ) ; o . body = this . body ; return o ; } , return { Object } api toJSON ( ) { return only ( this , [ 'status' , 'message' , 'header' ] ) ; } , end ( ) { } , } ;