import { Source , parse as parseGraphql , validate as validateGraphql , execute as executeGraphql , formatError as defaultFormatError , GraphQLError , GraphQLSchema , print as printGraphql , specifiedRules , DocumentNode , } from 'graphql' ; import { extendedFormatError } from '../extendedFormatError' ; import { IncomingMessage , ServerResponse } from 'http' ; import { pluginHookFromOptions } from '../pluginHook' ; import { HttpRequestHandler , mixed , CreateRequestHandlerOptions } from '../../interfaces' ; import setupServerSentEvents from './setupServerSentEvents' ; import withPostGraphileContext from '../withPostGraphileContext' ; import LRU from '@graphile/lru' ; import chalk from 'chalk' ; import Debugger = require ( 'debug' ) ; import httpError = require ( 'http-errors' ) ; import parseUrl = require ( 'parseurl' ) ; import finalHandler = require ( 'finalhandler' ) ; import bodyParser = require ( 'body-parser' ) ; import crypto = require ( 'crypto' ) ; const isKoaApp = ( a , b ) => a . req && a . res && typeof b === 'function' ; const CACHE_MULTIPLIER = 100000 ; const ALLOW_EXPLAIN_PLACEHOLDER = '__SHOULD_ALLOW_EXPLAIN__' ; const noop = ( ) => { } ; const { createHash } = crypto ; type { Buffer } import favicon from '../../assets/favicon.ico' ; /**
 * The GraphiQL HTML file as a string. We need it to be a string, because we
 * will use a regular expression to replace some variables.
 */ import baseGraphiqlHtml from '../../assets/graphiql.html' ; import { enhanceHttpServerWithWebSockets } from './subscriptions' ; import { CompatKoaContext , CompatKoaNext , PostGraphileResponse , PostGraphileResponseKoa , PostGraphileResponseNode , } from './frameworks' ; /**
 * When writing JSON to the browser, we need to be careful that it doesn't get
 * interpretted as HTML.
 */ const JS_ESCAPE_LOOKUP = { '<' : '\\u003c' , '>' : '\\u003e' , '/' : '\\u002f' , '\u2028' : '\\u2028' , '\u2029' : '\\u2029' , } ; function safeJSONStringify ( obj ) { return JSON . stringify ( obj ) . replace ( /[<>/\u2028\u2029]/g , chr => JS_ESCAPE_LOOKUP [ chr ] ) ; } /**
 * When people webpack us up, e.g. for lambda, if they don't want GraphiQL then
 * they can seriously reduce bundle size by omitting the assets.
 */ const shouldOmitAssets = process . env . POSTGRAPHILE_OMIT_ASSETS === '1' ; let lastString ; let lastHash ; const calculateQueryHash = ( queryString ) : string => { if ( queryString !== lastString ) { lastString = queryString ; lastHash = createHash ( 'sha1' ) . update ( queryString ) . digest ( 'base64' ) ; } return lastHash ; } ; export function isEmpty ( value ) { for ( const _key in value ) { return false ; } return true ; } const isPostGraphileDevelopmentMode = process . env . POSTGRAPHILE_ENV === 'development' ; const debugGraphql = Debugger ( 'postgraphile:graphql' ) ; const debugRequest = Debugger ( 'postgraphile:request' ) ; /**
 * We need to be able to share the withPostGraphileContext logic between HTTP
 * and websockets
 */ function withPostGraphileContextFromReqResGenerator ( options ) { const { pgSettings : pgSettingsGenerator , allowExplain : allowExplainGenerator , jwtSecret , jwtPublicKey , additionalGraphQLContextFromRequest , } = options ; return async ( req , res , moreOptions , fn ) => { const jwtVerificationSecret = jwtPublicKey || jwtSecret ; const jwtToken = jwtVerificationSecret ? getJwtToken ( req ) : null ; const additionalContext = typeof additionalGraphQLContextFromRequest === 'function' ? await additionalGraphQLContextFromRequest ( req , res ) : null ; const pgSettings = typeof pgSettingsGenerator === 'function' ? await pgSettingsGenerator ( req ) : pgSettingsGenerator ; const allowExplain = typeof allowExplainGenerator === 'function' ? await allowExplainGenerator ( req ) : allowExplainGenerator ; return withPostGraphileContext ( { ... options , jwtToken , pgSettings , explain : allowExplain && req . headers [ 'x-postgraphile-explain' ] === 'on' , ... moreOptions , } , context => { const graphqlContext = additionalContext ? { ... additionalContext , ... ( context as Record < string , any > ) } : context ; return fn ( graphqlContext ) ; } ) ; } ; } /**
 * Creates a GraphQL request handler that can support many different `http` frameworks, including:
 *
 * - Native Node.js `http`.
 * - `connect`.
 * - `express`.
 * - `koa` (2.0).
 */ export default function createPostGraphileHttpRequestHandler ( options ) { const MEGABYTE = 1024 * 1024 ; const subscriptions = ! ! options . subscriptions ; const { getGqlSchema , pgPool , pgSettings , pgDefaultRole , shutdownActions , queryCacheMaxSize = 50 * MEGABYTE , extendedErrors , showErrorStack , watchPg , disableQueryLog , enableQueryBatching , websockets = options . subscriptions || options . live ? [ 'v0' , 'v1' ] : [ ] , } = options ; const live = ! ! options . live ; const enhanceGraphiql = options . enhanceGraphiql === false ? false : ! ! options . enhanceGraphiql || subscriptions || live ; const enableCors = ! ! options . enableCors || isPostGraphileDevelopmentMode ; const graphiql = options . graphiql === true ; if ( options [ 'absoluteRoutes' ] ) { throw new Error ( 'Sorry - the `absoluteRoutes` setting has been replaced with `externalUrlBase` which solves the issue in a cleaner way. Please update your settings. Thank you for testing a PostGraphile pre-release 🙏' ) ; } let externalUrlBase = options . externalUrlBase ; if ( externalUrlBase && externalUrlBase . endsWith ( '/' ) ) { throw new Error ( 'externalUrlBase must not end with a slash (`/`)' ) ; } if ( ! Array . isArray ( websockets ) || ( websockets . length && websockets . some ( ver => ! [ 'v0' , 'v1' ] . includes ( ver ) ) ) ) { throw new Error ( `Invalid value for \`websockets\` option: '${ websockets }'` ) ; } const pluginHook = pluginHookFromOptions ( options ) ; const origGraphiqlHtml = pluginHook ( 'postgraphile:graphiql:html' , baseGraphiqlHtml , { options } ) ; if ( pgDefaultRole && typeof pgSettings === 'function' ) { throw new Error ( 'pgDefaultRole cannot be combined with pgSettings(req) - please remove pgDefaultRole and instead always return a `role` key from pgSettings(req).' ) ; } if ( pgDefaultRole && pgSettings && typeof pgSettings === 'object' && Object . keys ( pgSettings ) . map ( s => s . toLowerCase ( ) ) . includes ( 'role' ) ) { throw new Error ( 'pgDefaultRole cannot be combined with pgSettings.role - please use one or the other.' ) ; } if ( graphiql && shouldOmitAssets ) { throw new Error ( 'Cannot enable GraphiQL when POSTGRAPHILE_OMIT_ASSETS is set' ) ; } const graphqlRoute = options . graphqlRoute || '/graphql' ; const graphiqlRoute = options . graphiqlRoute || '/graphiql' ; const graphiqlCredentials = options . graphiqlCredentials || 'same-origin' ; const eventStreamRoute = options . eventStreamRoute || `${ graphqlRoute . replace ( /\/+$/ , '' ) }/stream` ; const externalGraphqlRoute = options . externalGraphqlRoute ; const externalEventStreamRoute = options . externalEventStreamRoute || ( externalGraphqlRoute && ! options . eventStreamRoute ? `${ externalGraphqlRoute . replace ( /\/+$/ , '' ) }/stream` : undefined ) ; if ( graphqlRoute === graphiqlRoute ) throw new Error ( `Cannot use the same route, '${ graphqlRoute }', for both GraphQL and GraphiQL. Please use different routes.` ) ; const formatError = ( error ) => { const formattedError = extendedErrors && extendedErrors . length ? extendedFormatError ( error , extendedErrors ) : defaultFormatError ( error ) ; if ( showErrorStack ) ( formattedError as Record < string , any > ) [ 'stack' ] = error . stack != null && showErrorStack === 'json' ? error . stack . split ( '\n' ) : error . stack ; return formattedError ; } ; const DEFAULT_HANDLE_ERRORS = ( errors ) => errors . map ( formatError ) ; const handleErrors = options . handleErrors || DEFAULT_HANDLE_ERRORS ; const bodyParserMiddlewares = [ bodyParser . json ( { limit : options . bodySizeLimit } ) , bodyParser . urlencoded ( { extended : false , limit : options . bodySizeLimit } ) , bodyParser . text ( { type : 'application/graphql' , limit : options . bodySizeLimit } ) , ] ; const bodyParserMiddlewaresComposed = bodyParserMiddlewares . reduce ( ( parent , fn ) : ( ( req : IncomingMessage , res : ServerResponse , next : ( err ? : Error ) => void ) => void ) => { return ( req , res , next ) => { parent ( req , res , error => { if ( error ) { return next ( error ) ; } fn ( req , res , next ) ; } ) ; } ; } , ( _req , _res , next ) => next ( ) ) ; const parseBody = ( req , res ) => new Promise < void > ( ( resolve , reject ) => { bodyParserMiddlewaresComposed ( req , res . getNodeServerResponse ( ) , ( error ) => { if ( error ) { reject ( error ) ; } else { resolve ( ) ; } } ) ; } ) ; let graphiqlHtml ; const withPostGraphileContextFromReqRes = withPostGraphileContextFromReqResGenerator ( options ) ; const staticValidationRules = pluginHook ( 'postgraphile:validationRules:static' , specifiedRules , { options , } ) ; interface CacheEntry { queryDocumentAst : DocumentNode ; validationErrors : ReadonlyArray < GraphQLError > ; length : number ; } const cacheSize = Math . ceil ( queryCacheMaxSize / CACHE_MULTIPLIER ) ; const cacheEnabled = cacheSize >= 2 ; const queryCache = cacheEnabled ? new LRU ( { maxLength : cacheSize } ) : null ; let lastGqlSchema ; const parseQuery = ( gqlSchema , queryString ) : { queryDocumentAst : DocumentNode ; validationErrors : ReadonlyArray < GraphQLError > ; } => { if ( gqlSchema !== lastGqlSchema ) { if ( queryCache ) { queryCache . reset ( ) ; } lastGqlSchema = gqlSchema ; } const canCache = cacheEnabled && queryString . length < 100000 ; const hash = canCache ? calculateQueryHash ( queryString ) : null ; const result = canCache ? queryCache ! . get ( hash ! ) : null ; if ( result ) { return result ; } else { const source = new Source ( queryString , 'GraphQL Http Request' ) ; let queryDocumentAst ; try { queryDocumentAst = parseGraphql ( source ) ; } catch ( error ) { error . statusCode = 400 ; throw error ; } if ( debugRequest . enabled ) debugRequest ( 'GraphQL query is parsed.' ) ; const validationErrors = validateGraphql ( gqlSchema , queryDocumentAst , staticValidationRules ) ; const cacheResult = { queryDocumentAst , validationErrors , length : queryString . length , } ; if ( canCache ) { queryCache ! . set ( hash ! , cacheResult ) ; } return cacheResult ; } } ; let firstRequestHandler = req => { firstRequestHandler = null ; let graphqlRouteForWs = graphqlRoute ; const { pathname = '' } = parseUrl ( req ) || { } ; const { pathname : originalPathname = '' } = parseUrl . original ( req ) || { } ; if ( originalPathname !== pathname && originalPathname . endsWith ( pathname ) ) { const base = originalPathname . substr ( 0 , originalPathname . length - pathname . length ) ; graphqlRouteForWs = base + graphqlRouteForWs ; if ( externalUrlBase == null ) { externalUrlBase = base ; } } externalUrlBase = externalUrlBase || '' ; graphiqlHtml = origGraphiqlHtml ? origGraphiqlHtml . replace ( /<\/head>/ , `  <script>window.POSTGRAPHILE_CONFIG=${ safeJSONStringify ( { graphqlUrl : externalGraphqlRoute || `${ externalUrlBase }${ graphqlRoute }` , streamUrl : watchPg ? externalEventStreamRoute || `${ externalUrlBase }${ eventStreamRoute }` : null , enhanceGraphiql , websockets : ! websockets . length ? 'none' : websockets . includes ( 'v1' ) ? 'v1' : 'v0' , allowExplain : typeof options . allowExplain === 'function' ? ALLOW_EXPLAIN_PLACEHOLDER : ! ! options . allowExplain , credentials : graphiqlCredentials , } ) };</script>\n  </head>` ) : null ; if ( websockets . length ) { const server = req && req . connection && req . connection [ 'server' ] ; if ( ! server ) { console . warn ( "Failed to find server to add websocket listener to, you'll need to call `enhanceHttpServerWithWebSockets` manually" ) ; } else { enhanceHttpServerWithWebSockets ( server , middleware , { graphqlRoute : graphqlRouteForWs , } ) ; } } } ; let theOneAndOnlyGraphQLSchema = null ; if ( ! watchPg ) { getGqlSchema ( ) . then ( schema => { theOneAndOnlyGraphQLSchema = schema ; } ) . catch ( noop ) ; } function neverReject ( middlewareName , middleware ) { return async ( res ) => { try { await middleware ( res ) ; } catch ( e ) { console . error ( `An unexpected error occurred whilst processing '${ middlewareName }'; this indicates a bug. The connection will be terminated.` ) ; console . error ( e ) ; try { res . statusCode = 500 ; res . end ( ) ; } catch ( e ) { } } } ; } /**
     * The actual request handler. It’s an async function so it will return a
     * promise when complete. If the function doesn’t handle anything, it calls
     * `next` to let the next middleware try and handle it. If the function
     * throws an error, it's up to the wrapping middleware (imaginatively named
     * `middleware`, below) to handle the error. Frameworks like Koa have
     * middlewares reject a promise on error, whereas Express requires you pass
     * the error to the `next(err)` function.
     */ const requestHandler = async ( responseHandler , next ) => { const res = responseHandler ; const incomingReq = res . getNodeServerRequest ( ) ; const nodeRes = res . getNodeServerResponse ( ) ; const req = pluginHook ( 'postgraphile:http:handler' , incomingReq , { options , res : nodeRes , next , } ) ; if ( req == null ) { return ; } const { pathname = '' } = parseUrl ( req ) || { } ; if ( firstRequestHandler ) firstRequestHandler ( req ) ; if ( watchPg ) { if ( pathname === eventStreamRoute || pathname === '/_postgraphile/stream' ) { return eventStreamRouteHandler ( res ) ; } } const isGraphqlRoute = pathname === graphqlRoute ; if ( ! shouldOmitAssets && graphiql && ! isGraphqlRoute ) { if ( pathname === '/favicon.ico' ) { return faviconRouteHandler ( res ) ; } if ( pathname === graphiqlRoute ) { if ( isPostGraphileDevelopmentMode ) { res . statusCode = 302 ; res . setHeader ( 'Location' , 'http://localhost:5783' ) ; res . end ( ) ; return ; } return graphiqlRouteHandler ( res ) ; } } if ( isGraphqlRoute ) { return graphqlRouteHandler ( res ) ; } else { return next ( ) ; } } ; const eventStreamRouteHandler = neverReject ( 'eventStreamRouteHandler' , async function eventStreamRouteHandler ( res ) { try { const req = pluginHook ( 'postgraphile:http:eventStreamRouteHandler' , res . getNodeServerRequest ( ) , { options , response : res } ) ; if ( req == null ) { return ; } if ( enableCors ) addCORSHeaders ( res ) ; if ( req . headers . accept !== 'text/event-stream' ) { res . statusCode = 405 ; res . end ( ) ; return ; } setupServerSentEvents ( res , options ) ; } catch ( e ) { console . error ( 'Unexpected error occurred in eventStreamRouteHandler' ) ; console . error ( e ) ; res . statusCode = 500 ; res . end ( ) ; } } ) ; const faviconRouteHandler = neverReject ( 'faviconRouteHandler' , async function faviconRouteHandler ( res ) { const req = pluginHook ( 'postgraphile:http:faviconRouteHandler' , res . getNodeServerRequest ( ) , { options , response : res , } ) ; if ( req == null ) { return ; } if ( ! ( req . method === 'GET' || req . method === 'HEAD' ) ) { res . statusCode = req . method === 'OPTIONS' ? 200 : 405 ; res . setHeader ( 'Allow' , 'GET, HEAD, OPTIONS' ) ; res . end ( ) ; return ; } res . statusCode = 200 ; res . setHeader ( 'Cache-Control' , 'public, max-age=86400' ) ; res . setHeader ( 'Content-Type' , 'image/x-icon' ) ; if ( req . method === 'HEAD' ) { res . end ( ) ; return ; } res . end ( favicon ) ; } ) ; const graphiqlRouteHandler = neverReject ( 'graphiqlRouteHandler' , async function graphiqlRouteHandler ( res ) { const req = pluginHook ( 'postgraphile:http:graphiqlRouteHandler' , res . getNodeServerRequest ( ) , { options , response : res , } ) ; if ( req == null ) { return ; } if ( firstRequestHandler ) firstRequestHandler ( req ) ; if ( ! ( req . method === 'GET' || req . method === 'HEAD' ) ) { res . statusCode = req . method === 'OPTIONS' ? 200 : 405 ; res . setHeader ( 'Allow' , 'GET, HEAD, OPTIONS' ) ; res . end ( ) ; return ; } res . statusCode = 200 ; res . setHeader ( 'Content-Type' , 'text/html; charset=utf-8' ) ; res . setHeader ( 'X-Frame-Options' , 'SAMEORIGIN' ) ; res . setHeader ( 'Content-Security-Policy' , "frame-ancestors 'self'" ) ; if ( req . method === 'HEAD' ) { res . end ( ) ; return ; } if ( graphiqlHtml && typeof options . allowExplain === 'function' ) { res . end ( graphiqlHtml . replace ( `"${ ALLOW_EXPLAIN_PLACEHOLDER }"` , JSON . stringify ( ! ! ( await options . allowExplain ( req ) ) ) ) ) ; } else { res . end ( graphiqlHtml ) ; } } ) ; const graphqlRouteHandler = neverReject ( 'graphqlRouteHandler' , async function graphqlRouteHandler ( res ) { const req = pluginHook ( 'postgraphile:http:graphqlRouteHandler' , res . getNodeServerRequest ( ) , { options , response : res , } ) ; if ( req == null ) { return ; } if ( firstRequestHandler ) firstRequestHandler ( req ) ; if ( enableCors ) addCORSHeaders ( res ) ; res . statusCode = 200 ; if ( watchPg ) { res . setHeader ( 'X-GraphQL-Event-Stream' , externalEventStreamRoute || `${ externalUrlBase }${ eventStreamRoute }` ) ; } if ( req . method === 'OPTIONS' ) { res . statusCode = 200 ; res . end ( ) ; return ; } let paramsList ; let results = [ ] ; const queryTimeStart = ! disableQueryLog && process . hrtime ( ) ; let pgRole ; if ( debugRequest . enabled ) debugRequest ( 'GraphQL query request has begun.' ) ; let returnArray = false ; try { const gqlSchema = theOneAndOnlyGraphQLSchema || ( await getGqlSchema ( ) ) ; await parseBody ( req , res ) ; if ( req . method !== 'POST' ) { res . setHeader ( 'Allow' , 'POST, OPTIONS' ) ; throw httpError ( 405 , 'Only `POST` requests are allowed.' ) ; } const body = ( req as any ) . body ; paramsList = typeof body === 'string' ? { query : body } : body ; if ( paramsList == null ) throw httpError ( 400 , 'Must provide an object parameters, not nullish value.' ) ; if ( typeof paramsList !== 'object' ) throw httpError ( 400 , `Expected parameter object, not value of type '${ typeof paramsList }'.` ) ; if ( Array . isArray ( paramsList ) ) { if ( ! enableQueryBatching ) { throw httpError ( 501 , 'Batching queries as an array is currently unsupported. Please provide a single query object.' ) ; } else { returnArray = true ; } } else { paramsList = [ paramsList ] ; } paramsList = pluginHook ( 'postgraphile:httpParamsList' , paramsList , { options , req , res , returnArray , httpError , } ) ; results = await Promise . all ( paramsList . map ( async ( params ) => { let queryDocumentAst = null ; let result ; const meta = Object . create ( null ) ; try { if ( ! params ) throw httpError ( 400 , 'Invalid query structure.' ) ; const { query , operationName } = params ; let { variables } = params ; if ( ! query ) throw httpError ( 400 , 'Must provide a query string.' ) ; if ( typeof variables === 'string' ) { if ( variables === '' ) { variables = null ; } else { try { variables = JSON . parse ( variables ) ; } catch ( error ) { error . statusCode = 400 ; throw error ; } } } if ( variables != null && typeof variables !== 'object' ) throw httpError ( 400 , `Variables must be an object, not '${ typeof variables }'.` ) ; if ( operationName != null && typeof operationName !== 'string' ) throw httpError ( 400 , `Operation name must be a string, not '${ typeof operationName }'.` ) ; let validationErrors ; ( { queryDocumentAst , validationErrors } = parseQuery ( gqlSchema , query ) ) ; if ( validationErrors . length === 0 ) { const moreValidationRules = pluginHook ( 'postgraphile:validationRules' , [ ] , { options , req , res , variables , operationName , meta , } ) ; if ( moreValidationRules . length ) { validationErrors = validateGraphql ( gqlSchema , queryDocumentAst , moreValidationRules ) ; } } if ( validationErrors . length > 0 ) { result = { errors : validationErrors , statusCode : 400 } ; } else if ( ! queryDocumentAst ) { throw new Error ( 'Could not process query' ) ; } else { if ( debugRequest . enabled ) debugRequest ( 'GraphQL query is validated.' ) ; if ( debugGraphql . enabled ) debugGraphql ( '%s' , printGraphql ( queryDocumentAst ) . replace ( /\s+/g , ' ' ) . trim ( ) ) ; result = await withPostGraphileContextFromReqRes ( req , res . getNodeServerResponse ( ) , { singleStatement : false , queryDocumentAst , variables , operationName , } , ( graphqlContext ) => { pgRole = graphqlContext . pgRole ; const graphqlResult = executeGraphql ( gqlSchema , queryDocumentAst ! , null , graphqlContext , variables , operationName ) ; if ( typeof graphqlContext . getExplainResults === 'function' ) { return Promise . resolve ( graphqlResult ) . then ( async ( obj ) => ( { ... obj , explain : await graphqlContext . getExplainResults ( ) , } ) ) ; } else { return graphqlResult ; } } ) ; } } catch ( error ) { result = { errors : [ error ] , statusCode : error . status || error . statusCode || 500 , } ; if ( result . statusCode === 500 ) console . error ( error . stack ) ; } finally { if ( result && result . errors ) { result . errors = ( handleErrors as any ) ( result . errors , req , res ) ; } if ( ! isEmpty ( meta ) ) { result . meta = meta ; } result = pluginHook ( 'postgraphile:http:result' , result , { options , returnArray , queryDocumentAst , req , pgRole , } ) ; if ( ! disableQueryLog && queryDocumentAst ) { const definitelyQueryDocumentAst = queryDocumentAst ; const resultStatusCode = result . statusCode ; const timeDiff = queryTimeStart && process . hrtime ( queryTimeStart ) ; setImmediate ( ( ) => { const prettyQuery = printGraphql ( definitelyQueryDocumentAst ) . replace ( /\s+/g , ' ' ) . trim ( ) ; const errorCount = ( result . errors || [ ] ) . length ; const ms = timeDiff [ 0 ] * 1e3 + timeDiff [ 1 ] * 1e-6 ; let message ; if ( resultStatusCode === 401 ) { message = chalk . red ( `401 authentication error` ) ; } else if ( resultStatusCode === 403 ) { message = chalk . red ( `403 forbidden error` ) ; } else { message = chalk [ errorCount === 0 ? 'green' : 'red' ] ( `${ errorCount } error(s)` ) ; } console . log ( `${ message } ${ pgRole != null ? `as ${ chalk . magenta ( pgRole ) } ` : '' }in ${ chalk . grey ( `${ ms . toFixed ( 2 ) }ms` ) } :: ${ prettyQuery }` ) ; } ) ; } if ( debugRequest . enabled ) debugRequest ( 'GraphQL query has been executed.' ) ; } return result ; } ) ) ; } catch ( error ) { if ( res . statusCode === 200 ) res . statusCode = error . status || error . statusCode || 500 ; returnArray = false ; results = [ { errors : ( handleErrors as any ) ( [ error ] , req , res ) } ] ; if ( res . statusCode === 500 ) { console . error ( error . stack ) ; } } finally { if ( ! returnArray ) { if ( res . statusCode === 200 && results [ 0 ] . statusCode ) { res . statusCode = results [ 0 ] . statusCode ; } results [ 0 ] . statusCode = undefined ; } res . setHeader ( 'Content-Type' , 'application/json; charset=utf-8' ) ; const { statusCode , result } = pluginHook ( 'postgraphile:http:end' , { statusCode : res . statusCode , result : returnArray ? results : results [ 0 ] ! , } , { options , returnArray , req , res : res . getNodeServerResponse ( ) , } ) ; if ( statusCode ) { res . statusCode = statusCode ; } res . end ( JSON . stringify ( result ) ) ; if ( debugRequest . enabled ) { debugRequest ( 'GraphQL ' + ( returnArray ? 'queries' : 'query' ) + ' request finished.' ) ; } } } ) ; /**
     * A polymorphic request handler that should detect what `http` framework is
     * being used and specifically handle that framework.
     *
     * Supported frameworks include:
     *
     * - Native Node.js `http`.
     * - `connect`.
     * - `express`.
     * - `koa` (2.0).
     */ const middleware = ( a , b , c ) => { if ( isKoaApp ( a , b ) ) { const ctx = a as CompatKoaContext ; const next = b as CompatKoaNext ; const responseHandler = new PostGraphileResponseKoa ( ctx , next ) ; return requestHandler ( responseHandler , next ) ; } else { const req = a as IncomingMessage ; const res = b as ServerResponse ; const next = c || finalHandler ( req , res ) ; const responseHandler = new PostGraphileResponseNode ( req , res , next ) ; requestHandler ( responseHandler , next ) . catch ( next ) ; } } ; middleware . getGraphQLSchema = getGqlSchema ; middleware . formatError = formatError ; middleware . pgPool = pgPool ; middleware . withPostGraphileContextFromReqRes = withPostGraphileContextFromReqRes ; middleware . handleErrors = handleErrors ; middleware . options = options ; middleware . graphqlRoute = graphqlRoute ; middleware . graphqlRouteHandler = graphqlRouteHandler ; middleware . graphiqlRoute = graphiqlRoute ; middleware . graphiqlRouteHandler = graphiql ? graphiqlRouteHandler : null ; middleware . faviconRouteHandler = graphiql ? faviconRouteHandler : null ; middleware . eventStreamRoute = eventStreamRoute ; middleware . eventStreamRouteHandler = watchPg ? eventStreamRouteHandler : null ; middleware . shutdownActions = shutdownActions ; middleware . release = ( ) : Promise < void > => shutdownActions . invokeAll ( ) ; const hookedMiddleware = pluginHook ( 'postgraphile:middleware' , middleware , { options , shutdownActions , } ) ; if ( ! hookedMiddleware . getGraphQLSchema ) { throw new Error ( "Hook for 'postgraphile:middleware' has not copied over the helpers; e.g. missing `Object.assign(newMiddleware, oldMiddleware)`" ) ; } return hookedMiddleware as HttpRequestHandler ; } /**
 * Adds CORS to a request. See [this][1] flowchart for an explanation of how
 * CORS works. Note that these headers are set for all requests, CORS
 * algorithms normally run a preflight request using the `OPTIONS` method to
 * get these headers.
 *
 * Note though, that enabling CORS will incur extra costs when it comes to the
 * preflight requests. It is much better if you choose to use a proxy and
 * bypass CORS altogether.
 *
 * [1]: http://www.html5rocks.com/static/images/cors_server_flowchart.png
 */ function addCORSHeaders ( res ) { res . setHeader ( 'Access-Control-Allow-Origin' , '*' ) ; res . setHeader ( 'Access-Control-Allow-Methods' , 'HEAD, GET, POST' ) ; res . setHeader ( 'Access-Control-Allow-Headers' , [ 'Origin' , 'X-Requested-With' , 'Accept' , 'Authorization' , 'X-Apollo-Tracing' , 'Content-Type' , 'Content-Length' , 'X-PostGraphile-Explain' , ] . join ( ', ' ) ) ; res . setHeader ( 'Access-Control-Expose-Headers' , [ 'X-GraphQL-Event-Stream' ] . join ( ', ' ) ) ; } function createBadAuthorizationHeaderError ( ) { return httpError ( 400 , 'Authorization header is not of the correct bearer scheme format.' ) ; } private const authorizationBearerRex = /^\s*bearer\s+([a-z0-9\-._~+/]+=*)\s*$/i ; private param { IncomingMessage } request returns { string | null } function getJwtToken ( request ) { const { authorization } = request . headers ; if ( Array . isArray ( authorization ) ) throw createBadAuthorizationHeaderError ( ) ; if ( authorization == null ) return null ; const match = authorizationBearerRex . exec ( authorization ) ; if ( ! match ) throw createBadAuthorizationHeaderError ( ) ; return match [ 1 ] ; }