UNPKG

@pyramation/postgraphile

Version:

A GraphQL schema created by reflection over a PostgreSQL schema 🐘 (previously known as PostGraphQL)

831 lines • 68.3 kB
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isEmpty = void 0;
/* eslint-disable @typescript-eslint/no-explicit-any,require-atomic-updates */
const graphql_1 = require("graphql");
const extendedFormatError_1 = require("../extendedFormatError");
const koaMiddleware_1 = require("./koaMiddleware");
const pluginHook_1 = require("../pluginHook");
const setupServerSentEvents_1 = require("./setupServerSentEvents");
const withPostGraphileContext_1 = require("../withPostGraphileContext");
const lru_1 = require("@graphile/lru");
const chalk_1 = require("chalk");
const Debugger = require("debug"); // tslint:disable-line variable-name
const httpError = require("http-errors");
const parseUrl = require("parseurl");
const finalHandler = require("finalhandler");
const bodyParser = require("body-parser");
const crypto = require("crypto");
const CACHE_MULTIPLIER = 100000;
const ALLOW_EXPLAIN_PLACEHOLDER = '__SHOULD_ALLOW_EXPLAIN__';
const noop = () => {
    /* noop */
};
const { createHash } = crypto;
/**
 * The favicon file in `Buffer` format. We can send a `Buffer` directly to the
 * client.
 *
 * @type {Buffer}
 */
const favicon_ico_1 = require("../../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.
 */
const graphiql_html_1 = require("../../assets/graphiql.html");
const subscriptions_1 = require("./subscriptions");
/**
 * 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';
// Used by `createPostGraphileHttpRequestHandler`
let lastString;
let lastHash;
const calculateQueryHash = (queryString) => {
    if (queryString !== lastString) {
        lastString = queryString;
        lastHash = createHash('sha1').update(queryString).digest('base64');
    }
    return lastHash;
};
// Fast way of checking if an object is empty,
// faster than `Object.keys(value).length === 0`.
// NOTE: we don't need a `hasOwnProperty` call here because isEmpty is called
// with an `Object.create(null)` object, so it has no no-own properties.
/* tslint:disable forin */
function isEmpty(value) {
    for (const _key in value) {
        return false;
    }
    return true;
}
exports.isEmpty = isEmpty;
/* tslint:enable forin */
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, additionalGraphQLContextFromRequest, } = options;
    return async (req, res, moreOptions, fn) => {
        const jwtToken = jwtSecret ? 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_1.default({
            ...options,
            jwtToken,
            pgSettings,
            explain: allowExplain && req.headers['x-postgraphile-explain'] === 'on',
            ...moreOptions,
        }, context => {
            const graphqlContext = additionalContext
                ? { ...additionalContext, ...context }
                : 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).
 */
function createPostGraphileHttpRequestHandler(options) {
    const MEGABYTE = 1024 * 1024;
    const { getGqlSchema, pgPool, pgSettings, pgDefaultRole, queryCacheMaxSize = 50 * MEGABYTE, extendedErrors, showErrorStack, watchPg, disableQueryLog, enableQueryBatching, } = options;
    const subscriptions = !!options.subscriptions;
    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 🙏');
    }
    // Using let because we might override it on the first request.
    let externalUrlBase = options.externalUrlBase;
    if (externalUrlBase && externalUrlBase.endsWith('/')) {
        throw new Error('externalUrlBase must not end with a slash (`/`)');
    }
    const pluginHook = pluginHook_1.pluginHookFromOptions(options);
    const origGraphiqlHtml = pluginHook('postgraphile:graphiql:html', graphiql_html_1.default, { 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');
    }
    // Gets the route names for our GraphQL endpoint, and our GraphiQL endpoint.
    const graphqlRoute = options.graphqlRoute || '/graphql';
    const graphiqlRoute = graphiql ? options.graphiqlRoute || '/graphiql' : null;
    const streamRoute = `${graphqlRoute.replace(/\/*$/, '')}/stream`;
    // Throw an error of the GraphQL and GraphiQL routes are the same.
    if (graphqlRoute === graphiqlRoute)
        throw new Error(`Cannot use the same route, '${graphqlRoute}', for both GraphQL and GraphiQL. Please use different routes.`);
    // Formats an error using the default GraphQL `formatError` function, and
    // custom formatting using some other options.
    const formatError = (error) => {
        // Get the appropriate formatted error object, including any extended error
        // fields if the user wants them.
        const formattedError = extendedErrors && extendedErrors.length
            ? extendedFormatError_1.extendedFormatError(error, extendedErrors)
            : graphql_1.formatError(error);
        // If the user wants to see the error’s stack, let’s add it to the
        // formatted error.
        if (showErrorStack)
            formattedError['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;
    function convertKoaBodyParserToConnect(req, _res, next) {
        if (req._koaCtx && req._koaCtx.request && req._koaCtx.request.body) {
            req._body = true;
            req.body = req._koaCtx.request.body;
        }
        next();
    }
    // Define a list of middlewares that will get run before our request handler.
    // Note though that none of these middlewares will intercept a request (i.e.
    // not call `next`). Middlewares that handle a request like favicon
    // middleware will result in a promise that never resolves, and we don’t
    // want that.
    const bodyParserMiddlewares = [
        // Convert koa body to connect-compatible body
        convertKoaBodyParserToConnect,
        // Parse JSON bodies.
        bodyParser.json({ limit: options.bodySizeLimit }),
        // Parse URL encoded bodies (forms).
        bodyParser.urlencoded({ extended: false, limit: options.bodySizeLimit }),
        // Parse `application/graphql` content type bodies as text.
        bodyParser.text({ type: 'application/graphql', limit: options.bodySizeLimit }),
    ];
    // We'll turn this into one function now so it can be better JIT optimised
    const bodyParserMiddlewaresComposed = bodyParserMiddlewares.reduce((parent, fn) => {
        return (req, res, next) => {
            parent(req, res, error => {
                if (error) {
                    return next(error);
                }
                fn(req, res, next);
            });
        };
    }, (_req, _res, next) => next());
    // And we really want that function to be await-able
    const parseBody = (req, res) => new Promise((resolve, reject) => {
        bodyParserMiddlewaresComposed(req, res, (error) => {
            if (error) {
                reject(error);
            }
            else {
                resolve();
            }
        });
    });
    // We only need to calculate the graphiql HTML once; but we need to receive the first request to do so.
    let graphiqlHtml;
    const withPostGraphileContextFromReqRes = withPostGraphileContextFromReqResGenerator(options);
    const staticValidationRules = pluginHook('postgraphile:validationRules:static', graphql_1.specifiedRules, {
        options,
    });
    const cacheSize = Math.ceil(queryCacheMaxSize / CACHE_MULTIPLIER);
    // Do not create an LRU for cache size < 2 because @graphile/lru will baulk.
    const cacheEnabled = cacheSize >= 2;
    const queryCache = cacheEnabled ? new lru_1.default({ maxLength: cacheSize }) : null;
    let lastGqlSchema;
    const parseQuery = (gqlSchema, queryString) => {
        if (gqlSchema !== lastGqlSchema) {
            if (queryCache) {
                queryCache.reset();
            }
            lastGqlSchema = gqlSchema;
        }
        // Only cache queries that are less than 100kB, we don't want DOS attacks
        // attempting to exhaust our memory.
        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 graphql_1.Source(queryString, 'GraphQL Http Request');
            let queryDocumentAst;
            // Catch an errors while parsing so that we can set the `statusCode` to
            // 400. Otherwise we don’t need to parse this way.
            try {
                queryDocumentAst = graphql_1.parse(source);
            }
            catch (error) {
                error.statusCode = 400;
                throw error;
            }
            if (debugRequest.enabled)
                debugRequest('GraphQL query is parsed.');
            // Validate our GraphQL query using given rules.
            const validationErrors = graphql_1.validate(gqlSchema, queryDocumentAst, staticValidationRules);
            const cacheResult = {
                queryDocumentAst,
                validationErrors,
                length: queryString.length,
            };
            if (canCache) {
                queryCache.set(hash, cacheResult);
            }
            return cacheResult;
        }
    };
    let firstRequestHandler = (req, pathname) => {
        // Never be called again
        firstRequestHandler = null;
        if (externalUrlBase == null) {
            // User hasn't specified externalUrlBase; let's try and guess it
            const { pathname: originalPathname = '' } = parseUrl.original(req) || {};
            if (originalPathname !== pathname && originalPathname.endsWith(pathname)) {
                // We were mounted on a subpath (e.g. `app.use('/path/to', postgraphile(...))`).
                // Figure out our externalUrlBase for ourselves.
                externalUrlBase = originalPathname.substr(0, originalPathname.length - pathname.length);
            }
            // Make sure we have a string, at least
            externalUrlBase = externalUrlBase || '';
        }
        // Takes the original GraphiQL HTML file and replaces the default config object.
        graphiqlHtml = origGraphiqlHtml
            ? origGraphiqlHtml.replace(/<\/head>/, `  <script>window.POSTGRAPHILE_CONFIG=${safeJSONStringify({
                graphqlUrl: `${externalUrlBase}${graphqlRoute}`,
                streamUrl: watchPg ? `${externalUrlBase}${streamRoute}` : null,
                enhanceGraphiql,
                subscriptions,
                allowExplain: typeof options.allowExplain === 'function'
                    ? ALLOW_EXPLAIN_PLACEHOLDER
                    : !!options.allowExplain,
            })};</script>\n  </head>`)
            : null;
        if (subscriptions) {
            const server = req && req.connection && req.connection['server'];
            if (!server) {
                // tslint:disable-next-line no-console
                console.warn("Failed to find server to add websocket listener to, you'll need to call `enhanceHttpServerWithSubscriptions` manually");
            }
            else {
                // Relying on this means that a normal request must come in before an
                // upgrade attempt. It's better to call it manually.
                subscriptions_1.enhanceHttpServerWithSubscriptions(server, middleware);
            }
        }
    };
    /*
     * If we're not in watch mode, then avoid the cost of `await`ing the schema
     * on every tick by having it available once it was generated.
     */
    let theOneAndOnlyGraphQLSchema = null;
    if (!watchPg) {
        getGqlSchema()
            .then(schema => {
            theOneAndOnlyGraphQLSchema = schema;
        })
            .catch(noop);
    }
    /**
     * 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.
     */
    const requestHandler = async (incomingReq, res, next) => {
        // You can use this hook either to modify the incoming request or to tell
        // PostGraphile not to handle the request further (return null). NOTE: if
        // you return `null` from this hook then you are also responsible for
        // calling `next()` (should that be required).
        const req = pluginHook('postgraphile:http:handler', incomingReq, {
            options,
            res,
            next,
        });
        if (req == null) {
            return;
        }
        // Add our CORS headers to be good web citizens (there are perf
        // implications though so be careful!)
        //
        // Always enable CORS when developing PostGraphile because GraphiQL will be
        // on port 5783.
        if (enableCors)
            addCORSHeaders(res);
        const { pathname = '' } = parseUrl(req) || {};
        // Certain things depend on externalUrlBase, which we guess if the user
        // doesn't supply it, so we calculate them on the first request. After
        // first request, this function becomes a NOOP
        if (firstRequestHandler)
            firstRequestHandler(req, pathname);
        const isGraphqlRoute = pathname === graphqlRoute;
        // ========================================================================
        // Serve GraphiQL and Related Assets
        // ========================================================================
        if (!shouldOmitAssets && graphiql && !isGraphqlRoute) {
            // ======================================================================
            // Favicon
            // ======================================================================
            // If this is the favicon path and it has not yet been handled, let us
            // serve our GraphQL favicon.
            if (pathname === '/favicon.ico') {
                // If this is the wrong method, we should let the client know.
                if (!(req.method === 'GET' || req.method === 'HEAD')) {
                    res.statusCode = req.method === 'OPTIONS' ? 200 : 405;
                    res.setHeader('Allow', 'GET, HEAD, OPTIONS');
                    res.end();
                    return;
                }
                // Otherwise we are good and should pipe the favicon to the browser.
                res.statusCode = 200;
                res.setHeader('Cache-Control', 'public, max-age=86400');
                res.setHeader('Content-Type', 'image/x-icon');
                // End early if the method is `HEAD`.
                if (req.method === 'HEAD') {
                    res.end();
                    return;
                }
                res.end(favicon_ico_1.default);
                return;
            }
            // ======================================================================
            // GraphiQL Watch Stream
            // ======================================================================
            // Setup an event stream so we can broadcast events to graphiql, etc.
            if (pathname === streamRoute || pathname === '/_postgraphile/stream') {
                if (!watchPg || req.headers.accept !== 'text/event-stream') {
                    res.statusCode = 405;
                    res.end();
                    return;
                }
                setupServerSentEvents_1.default(req, res, options);
                return;
            }
            // ======================================================================
            // GraphiQL HTML
            // ======================================================================
            // If this is the GraphiQL route, show GraphiQL and stop execution.
            if (pathname === graphiqlRoute) {
                // If we are developing PostGraphile, instead just redirect.
                if (isPostGraphileDevelopmentMode) {
                    res.statusCode = 302;
                    res.setHeader('Location', 'http://localhost:5783');
                    res.end();
                    return;
                }
                // If using the incorrect method, let the user know.
                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'");
                // End early if the method is `HEAD`.
                if (req.method === 'HEAD') {
                    res.end();
                    return;
                }
                // Actually renders GraphiQL.
                if (graphiqlHtml && typeof options.allowExplain === 'function') {
                    res.end(graphiqlHtml.replace(`"${ALLOW_EXPLAIN_PLACEHOLDER}"`, // Because JSON escaped
                    JSON.stringify(!!(await options.allowExplain(req)))));
                }
                else {
                    res.end(graphiqlHtml);
                }
                return;
            }
        }
        // Don’t handle any requests if this is not the correct route.
        if (!isGraphqlRoute)
            return next();
        // ========================================================================
        // Execute GraphQL Queries
        // ========================================================================
        // If we didn’t call `next` above, all requests will return 200 by default!
        res.statusCode = 200;
        if (watchPg) {
            // Inform GraphiQL and other clients that they can subscribe to events
            // (such as the schema being updated) at the following URL
            res.setHeader('X-GraphQL-Event-Stream', `${externalUrlBase}${streamRoute}`);
        }
        // Don’t execute our GraphQL stuffs for `OPTIONS` requests.
        if (req.method === 'OPTIONS') {
            res.statusCode = 200;
            res.end();
            return;
        }
        // The `result` will be used at the very end in our `finally` block.
        // Statements inside the `try` will assign to `result` when they get
        // a result. We also keep track of `params`.
        let paramsList;
        let results = [];
        const queryTimeStart = !disableQueryLog && process.hrtime();
        let pgRole;
        if (debugRequest.enabled)
            debugRequest('GraphQL query request has begun.');
        let returnArray = false;
        // This big `try`/`catch`/`finally` block represents the execution of our
        // GraphQL query. All errors thrown in this block will be returned to the
        // client as GraphQL errors.
        try {
            // First thing we need to do is get the GraphQL schema for this request.
            // It should never really change unless we are in watch mode.
            const gqlSchema = theOneAndOnlyGraphQLSchema || (await getGqlSchema());
            // Note that we run our middleware after we make sure we are on the
            // correct route. This is so that if our middleware modifies the `req` or
            // `res` objects, only we downstream will see the modifications.
            //
            // We also run our middleware inside the `try` so that we get the GraphQL
            // error reporting style for syntax errors.
            await parseBody(req, res);
            // If this is not one of the correct methods, throw an error.
            if (req.method !== 'POST') {
                res.setHeader('Allow', 'POST, OPTIONS');
                throw httpError(405, 'Only `POST` requests are allowed.');
            }
            // Get the parameters we will use to run a GraphQL request. `params` may
            // include:
            //
            // - `query`: The required GraphQL query string.
            // - `variables`: An optional JSON object containing GraphQL variables.
            // - `operationName`: The optional name of the GraphQL operation we will
            //   be executing.
            const body = req.body;
            paramsList = typeof body === 'string' ? { query: body } : body;
            // Validate our paramsList object a bit.
            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 variables is a string, we assume it is a JSON string and that it
                    // needs to be parsed.
                    if (typeof variables === 'string') {
                        // If variables is just an empty string, we should set it to null and
                        // ignore it.
                        if (variables === '') {
                            variables = null;
                        }
                        else {
                            // Otherwise, let us try to parse it as JSON.
                            try {
                                variables = JSON.parse(variables);
                            }
                            catch (error) {
                                error.statusCode = 400;
                                throw error;
                            }
                        }
                    }
                    // Throw an error if `variables` is not an object.
                    if (variables != null && typeof variables !== 'object')
                        throw httpError(400, `Variables must be an object, not '${typeof variables}'.`);
                    // Throw an error if `operationName` is not a string.
                    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) {
                        // You are strongly encouraged to use
                        // `postgraphile:validationRules:static` if possible - you should
                        // only use this one if you need access to variables.
                        const moreValidationRules = pluginHook('postgraphile:validationRules', [], {
                            options,
                            req,
                            res,
                            variables,
                            operationName,
                            meta,
                        });
                        if (moreValidationRules.length) {
                            validationErrors = graphql_1.validate(gqlSchema, queryDocumentAst, moreValidationRules);
                        }
                    }
                    // If we have some validation errors, don’t execute the query. Instead
                    // send the errors to the client with a `400` code.
                    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.');
                        // Lazily log the query. If this debugger isn’t enabled, don’t run it.
                        if (debugGraphql.enabled)
                            debugGraphql('%s', graphql_1.print(queryDocumentAst).replace(/\s+/g, ' ').trim());
                        result = await withPostGraphileContextFromReqRes(req, res, {
                            singleStatement: false,
                            queryDocumentAst,
                            variables,
                            operationName,
                        }, (graphqlContext) => {
                            pgRole = graphqlContext.pgRole;
                            const graphqlResult = graphql_1.execute(gqlSchema, queryDocumentAst, null, graphqlContext, variables, operationName);
                            if (typeof graphqlContext.getExplainResults === 'function') {
                                return Promise.resolve(graphqlResult).then(async (obj) => ({
                                    ...obj,
                                    // Add our explain data
                                    explain: await graphqlContext.getExplainResults(),
                                }));
                            }
                            else {
                                return graphqlResult;
                            }
                        });
                    }
                }
                catch (error) {
                    result = {
                        errors: [error],
                        statusCode: error.status || error.statusCode || 500,
                    };
                    // If the status code is 500, let’s log our error.
                    if (result.statusCode === 500)
                        // tslint:disable-next-line no-console
                        console.error(error.stack);
                }
                finally {
                    // Format our errors so the client doesn’t get the full thing.
                    if (result && result.errors) {
                        result.errors = handleErrors(result.errors, req, res);
                    }
                    if (!isEmpty(meta)) {
                        result.meta = meta;
                    }
                    result = pluginHook('postgraphile:http:result', result, {
                        options,
                        returnArray,
                        queryDocumentAst,
                        req,
                        pgRole,
                    });
                    // Log the query. If this debugger isn’t enabled, don’t run it.
                    if (!disableQueryLog && queryDocumentAst) {
                        // To appease TypeScript
                        const definitelyQueryDocumentAst = queryDocumentAst;
                        // We must reference this before it's deleted!
                        const resultStatusCode = result.statusCode;
                        const timeDiff = queryTimeStart && process.hrtime(queryTimeStart);
                        setImmediate(() => {
                            const prettyQuery = graphql_1.print(definitelyQueryDocumentAst)
                                .replace(/\s+/g, ' ')
                                .trim();
                            const errorCount = (result.errors || []).length;
                            const ms = timeDiff[0] * 1e3 + timeDiff[1] * 1e-6;
                            let message;
                            if (resultStatusCode === 401) {
                                // Users requested that JWT errors were raised differently:
                                //
                                //   https://github.com/graphile/postgraphile/issues/560
                                message = chalk_1.default.red(`401 authentication error`);
                            }
                            else if (resultStatusCode === 403) {
                                message = chalk_1.default.red(`403 forbidden error`);
                            }
                            else {
                                message = chalk_1.default[errorCount === 0 ? 'green' : 'red'](`${errorCount} error(s)`);
                            }
                            // tslint:disable-next-line no-console
                            console.log(`${message} ${pgRole != null ? `as ${chalk_1.default.magenta(pgRole)} ` : ''}in ${chalk_1.default.grey(`${ms.toFixed(2)}ms`)} :: ${prettyQuery}`);
                        });
                    }
                    if (debugRequest.enabled)
                        debugRequest('GraphQL query has been executed.');
                }
                return result;
            }));
        }
        catch (error) {
            // Set our status code and send the client our results!
            if (res.statusCode === 200)
                res.statusCode = error.status || error.statusCode || 500;
            // Overwrite entire response
            returnArray = false;
            results = [{ errors: handleErrors([error], req, res) }];
            // If the status code is 500, let’s log our error.
            if (res.statusCode === 500)
                // tslint:disable-next-line no-console
                console.error(error.stack);
        }
        finally {
            // Finally, we send the client the results.
            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,
            });
            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 are arguments look like the arguments to koa middleware, this is
        // `koa` middleware.
        if (koaMiddleware_1.isKoaApp(a, b)) {
            // Set the correct `koa` variable names…
            const ctx = a;
            const next = b;
            return koaMiddleware_1.middleware(ctx, next, requestHandler);
        }
        else {
            // Set the correct `connect` style variable names. If there was no `next`
            // defined (likely the case if the client is using `http`) we use the
            // final handler.
            const req = a;
            const res = b;
            const next = c || finalHandler(req, res);
            // Execute our request handler. If the request errored out, call `next` with the error.
            requestHandler(req, res, next).catch(next);
        }
    };
    middleware.getGraphQLSchema = getGqlSchema;
    middleware.formatError = formatError;
    middleware.pgPool = pgPool;
    middleware.withPostGraphileContextFromReqRes = withPostGraphileContextFromReqRes;
    middleware.handleErrors = handleErrors;
    middleware.options = options;
    const hookedMiddleware = pluginHook('postgraphile:middleware', middleware, {
        options,
    });
    // Sanity check:
    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;
}
exports.default = createPostGraphileHttpRequestHandler;
/**
 * 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',
        // Used by `express-graphql` to determine whether to expose the GraphiQL
        // interface (`text/html`) or not.
        'Accept',
        // Used by PostGraphile for auth purposes.
        'Authorization',
        // Used by GraphQL Playground and other Apollo-enabled servers
        'X-Apollo-Tracing',
        // The `Content-*` headers are used when making requests with a body,
        // like in a POST request.
        'Content-Type',
        'Content-Length',
        // For our 'Explain' feature
        '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.');
}
/**
 * Parses the `Bearer` auth scheme token out of the `Authorization` header as
 * defined by [RFC7235][1].
 *
 * ```
 * Authorization = credentials
 * credentials   = auth-scheme [ 1*SP ( token68 / #auth-param ) ]
 * token68       = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" )*"="
 * ```
 *
 * [1]: https://tools.ietf.org/html/rfc7235
 *
 * @private
 */
const authorizationBearerRex = /^\s*bearer\s+([a-z0-9\-._~+/]+=*)\s*$/i;
/**
 * Gets the JWT token from the Http request’s headers. Specifically the
 * `Authorization` header in the `Bearer` format. Will throw an error if the
 * header is in the incorrect format, but will not throw an error if the header
 * does not exist.
 *
 * @private
 * @param {IncomingMessage} request
 * @returns {string | null}
 */
function getJwtToken(request) {
    const { authorization } = request.headers;
    if (Array.isArray(authorization))
        throw createBadAuthorizationHeaderError();
    // If there was no authorization header, just return null.
    if (authorization == null)
        return null;
    const match = authorizationBearerRex.exec(authorization);
    // If we did not match the authorization header with our expected format,
    // throw a 400 error.
    if (!match)
        throw createBadAuthorizationHeaderError();
    // Return the token from our match.
    return match[1];
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlUG9zdEdyYXBoaWxlSHR0cFJlcXVlc3RIYW5kbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3Bvc3RncmFwaGlsZS9odHRwL2NyZWF0ZVBvc3RHcmFwaGlsZUh0dHBSZXF1ZXN0SGFuZGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw4RUFBOEU7QUFDOUUscUNBV2lCO0FBQ2pCLGdFQUE2RDtBQUU3RCxtREFBd0U7QUFDeEUsOENBQXNEO0FBRXRELG1FQUE0RDtBQUM1RCx3RUFBaUU7QUFFakUsdUNBQWdDO0FBRWhDLGlDQUEwQjtBQUMxQixrQ0FBbUMsQ0FBQyxvQ0FBb0M7QUFDeEUseUNBQTBDO0FBQzFDLHFDQUFzQztBQUN0Qyw2Q0FBOEM7QUFDOUMsMENBQTJDO0FBQzNDLGlDQUFrQztBQUVsQyxNQUFNLGdCQUFnQixHQUFHLE1BQU0sQ0FBQztBQUVoQyxNQUFNLHlCQUF5QixHQUFHLDBCQUEwQixDQUFDO0FBQzdELE1BQU0sSUFBSSxHQUFHLEdBQUcsRUFBRTtJQUNoQixVQUFVO0FBQ1osQ0FBQyxDQUFDO0FBRUYsTUFBTSxFQUFFLFVBQVUsRUFBRSxHQUFHLE1BQU0sQ0FBQztBQUU5Qjs7Ozs7R0FLRztBQUNILDBEQUErQztBQUUvQzs7O0dBR0c7QUFDSCw4REFBMEQ7QUFDMUQsbURBQXFFO0FBRXJFOzs7R0FHRztBQUNILE1BQU0sZ0JBQWdCLEdBQUc7SUFDdkIsR0FBRyxFQUFFLFNBQVM7SUFDZCxHQUFHLEVBQUUsU0FBUztJQUNkLEdBQUcsRUFBRSxTQUFTO0lBQ2QsUUFBUSxFQUFFLFNBQVM7SUFDbkIsUUFBUSxFQUFFLFNBQVM7Q0FDcEIsQ0FBQztBQUNGLFNBQVMsaUJBQWlCLENBQUMsR0FBd0I7SUFDakQsT0FBTyxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxvQkFBb0IsRUFBRSxHQUFHLENBQUMsRUFBRSxDQUFDLGdCQUFnQixDQUFD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