#!/usr/bin/env node
"use strict";
var path_1 = require("path");
var fs_1 = require("fs");
var http_1 = require("http");
var chalk = require("chalk");
var program = require("commander");
var pg_connection_string_1 = require("pg-connection-string");
var postgraphql_1 = require("./postgraphql");
// tslint:disable no-console
var config = {};
try {
    config = require(process.cwd() + '/.postgraphilerc'); // tslint:disable-line no-var-requires
    if (!config.hasOwnProperty('options')) {
        console.warn('WARNING: Your configuration file does not export any options');
    }
}
catch (error) {
}
// TODO: Demo Postgres database
var DEMO_PG_URL = null;
var manifest = JSON.parse(fs_1.readFileSync(path_1.resolve(__dirname, '../../package.json')).toString());
program
    .version(manifest.version)
    .usage('[options...]')
    .description(manifest.description)
    .option('-c, --connection <string>', 'the Postgres connection. if not provided it will be inferred from your environment, example: postgres://user:password@domain:port/db')
    .option('-s, --schema <string>', 'a Postgres schema to be introspected. Use commas to define multiple schemas', function (option) { return option.split(','); })
    .option('-w, --watch', 'watches the Postgres schema for changes and reruns introspection if a change was detected')
    .option('-n, --host <string>', 'the hostname to be used. Defaults to `localhost`')
    .option('-p, --port <number>', 'the port to be used. Defaults to 5000', parseFloat)
    .option('-m, --max-pool-size <number>', 'the maximum number of clients to keep in the Postgres pool. defaults to 10', parseFloat)
    .option('-r, --default-role <string>', 'the default Postgres role to use when a request is made. supercedes the role used to connect to the database')
    .option('-q, --graphql <path>', 'the route to mount the GraphQL server on. defaults to `/graphql`')
    .option('-i, --graphiql <path>', 'the route to mount the GraphiQL interface on. defaults to `/graphiql`')
    .option('-b, --disable-graphiql', 'disables the GraphiQL interface. overrides the GraphiQL route option')
    .option('--token <identifier>', 'DEPRECATED: use --jwt-token-identifier instead')
    .option('-o, --cors', 'enable generous CORS settings. this is disabled by default, if possible use a proxy instead')
    .option('-a, --classic-ids', 'use classic global id field name. required to support Relay 1')
    .option('-j, --dynamic-json', 'enable dynamic JSON in GraphQL inputs and outputs. uses stringified JSON by default')
    .option('-M, --disable-default-mutations', 'disable default mutations, mutation will only be possible through Postgres functions')
    .option('-l, --body-size-limit <string>', 'set the maximum size of JSON bodies that can be parsed (default 100kB) The size can be given as a human-readable string, such as \'200kB\' or \'5MB\' (case insensitive).')
    .option('--secret <string>', 'DEPRECATED: Use jwt-secret instead')
    .option('-e, --jwt-secret <string>', 'the secret to be used when creating and verifying JWTs. if none is provided auth will be disabled')
    .option('--jwt-audiences <string>', 'DEPRECATED Use jwt-verify-audience instead', function (option) { return option.split(','); })
    .option('--jwt-verify-algorithms <string>', 'a comma separated list of the names of the allowed jwt token algorithms', function (option) { return option.split(','); })
    .option('-A, --jwt-verify-audience <string>', 'a comma separated list of audiences your jwt token can contain. If no audience is given the audience defaults to `postgraphql`', function (option) { return option.split(','); })
    .option('--jwt-verify-clock-tolerance <number>', 'number of seconds to tolerate when checking the nbf and exp claims, to deal with small clock differences among different servers', parseFloat)
    .option('--jwt-verify-id <string>', 'the name of the allowed jwt token id')
    .option('--jwt-verify-ignore-expiration', 'if `true` do not validate the expiration of the token defaults to `false`')
    .option('--jwt-verify-ignore-not-before', 'if `true` do not validate the notBefore of the token defaults to `false`')
    .option('--jwt-verify-issuer <string>', 'a comma separated list of the names of the allowed jwt token issuer', function (option) { return option.split(','); })
    .option('--jwt-verify-subject <string>', 'the name of the allowed jwt token subject')
    .option('--jwt-role <string>', 'a comma seperated list of strings that create a path in the jwt from which to extract the postgres role. if none is provided it will use the key `role` on the root of the jwt.', function (option) { return option.split(','); })
    .option('-t, --jwt-token-identifier <identifier>', 'the Postgres identifier for a composite type that will be used to create JWT tokens')
    .option('--append-plugins <string>', 'a comma-separated list of plugins to append to the list of GraphQL schema plugins')
    .option('--prepend-plugins <string>', 'a comma-separated list of plugins to prepend to the list of GraphQL schema plugins')
    .option('--export-schema-json <path>', 'enables exporting the detected schema, in JSON format, to the given location. The directories must exist already, if the file exists it will be overwritten.')
    .option('--export-schema-graphql <path>', 'enables exporting the detected schema, in GraphQL schema format, to the given location. The directories must exist already, if the file exists it will be overwritten.')
    .option('--show-error-stack [setting]', 'show JavaScript error stacks in the GraphQL result errors')
    .option('--extended-errors <string>', 'a comma separated list of extended Postgres error fields to display in the GraphQL result. Example: \'hint,detail,errcode\'. Default: none', function (option) { return option.split(',').filter(function (_) { return _; }); })
    .option('--write-cache <path>', 'writes computed values to local cache file so startup can be faster (do this during the build phase)')
    .option('--read-cache <path>', 'reads cached values from local cache file to improve startup time (you may want to do this in production)');
program.on('--help', function () { return console.log("\n  Get Started:\n\n    $ postgraphql --demo\n    $ postgraphql --schema my_schema\n".slice(1)); });
program.parse(process.argv);
// Kill server on exit.
process.on('SIGINT', process.exit);
// Destruct our configuration file and command line arguments, use defaults, and rename options to
// something appropriate for JavaScript.
var _a = Object.assign({}, config['options'], program), _b = _a.demo, isDemo = _b === void 0 ? false : _b, pgConnectionString = _a.connection, watchPg = _a.watch, dbSchema = _a.schema, _c = _a.host, hostname = _c === void 0 ? 'localhost' : _c, _d = _a.port, port = _d === void 0 ? 5000 : _d, maxPoolSize = _a.maxPoolSize, pgDefaultRole = _a.defaultRole, _e = _a.graphql, graphqlRoute = _e === void 0 ? '/graphql' : _e, _f = _a.graphiql, graphiqlRoute = _f === void 0 ? '/graphiql' : _f, _g = _a.disableGraphiql, disableGraphiql = _g === void 0 ? false : _g, deprecatedJwtSecret = _a.secret, jwtSecret = _a.jwtSecret, jwtAudiences = _a.jwtAudiences, jwtVerifyAlgorithms = _a.jwtVerifyAlgorithms, jwtVerifyAudience = _a.jwtVerifyAudience, jwtVerifyClockTolerance = _a.jwtVerifyClockTolerance, jwtVerifyId = _a.jwtVerifyId, jwtVerifyIgnoreExpiration = _a.jwtVerifyIgnoreExpiration, jwtVerifyIgnoreNotBefore = _a.jwtVerifyIgnoreNotBefore, jwtVerifyIssuer = _a.jwtVerifyIssuer, jwtVerifySubject = _a.jwtVerifySubject, _h = _a.jwtRole, jwtRole = _h === void 0 ? ['role'] : _h, deprecatedJwtPgTypeIdentifier = _a.token, jwtPgTypeIdentifier = _a.jwtTokenIdentifier, _j = _a.cors, enableCors = _j === void 0 ? false : _j, _k = _a.classicIds, classicIds = _k === void 0 ? false : _k, _l = _a.dynamicJson, dynamicJson = _l === void 0 ? false : _l, _m = _a.disableDefaultMutations, disableDefaultMutations = _m === void 0 ? false : _m, exportJsonSchemaPath = _a.exportSchemaJson, exportGqlSchemaPath = _a.exportSchemaGraphql, showErrorStack = _a.showErrorStack, _o = _a.extendedErrors, extendedErrors = _o === void 0 ? [] : _o, bodySizeLimit = _a.bodySizeLimit, appendPluginNames = _a.appendPlugins, prependPluginNames = _a.prependPlugins, 
// replaceAllPlugins is NOT exposed via the CLI
readCache = _a.readCache, writeCache = _a.writeCache;
// Add custom logic for getting the schemas from our CLI. If we are in demo
// mode, we want to use the `forum_example` schema. Otherwise the `public`
// schema is what we want.
var schemas = dbSchema || (isDemo ? ['forum_example'] : ['public']);
// Create our Postgres config.
var pgConfig = Object.assign({}, 
// If we have a Postgres connection string, parse it and use that as our
// config. If we don’t have a connection string use some environment
// variables or final defaults. Other environment variables should be
// detected and used by `pg`.
pgConnectionString || process.env.DATABASE_URL || isDemo ?
    pg_connection_string_1.parse(pgConnectionString || process.env.DATABASE_URL || DEMO_PG_URL) : {
    host: process.env.PGHOST || 'localhost',
    port: process.env.PGPORT || 5432,
    database: process.env.PGDATABASE,
    user: process.env.PGUSER,
    password: process.env.PGPASSWORD,
}, 
// Add the max pool size to our config.
{ max: maxPoolSize });
var loadPlugins = function (rawNames) {
    if (!rawNames) {
        return undefined;
    }
    var names = String(rawNames).split(',');
    return names.map(function (rawName) {
        var name = String(rawName);
        var parts = name.split(':');
        var root;
        try {
            root = require(String(parts.shift()));
        }
        catch (e) {
            // tslint:disable-next-line no-console
            console.error("Failed to load plugin '" + name + "'");
            throw e;
        }
        var plugin = root;
        while (true) {
            var part = parts.shift();
            if (part == null) {
                break;
            }
            plugin = root[part];
            if (plugin == null) {
                throw new Error("No plugin found matching spec '" + name + "' - failed at '" + part + "'");
            }
        }
        if (typeof plugin === 'function') {
            return plugin;
        }
        else if (plugin === root && typeof plugin.default === 'function') {
            return plugin.default; // ES6 workaround
        }
        else {
            throw new Error("No plugin found matching spec '" + name + "' - expected function, found '" + typeof plugin + "'");
        }
    });
};
if (jwtAudiences && jwtVerifyAudience) {
    throw new Error("Provide either '--jwt-audiences' or '-A, --jwt-verify-audience' but not both");
}
var jwtVerifyOptions = {
    algorithms: jwtVerifyAlgorithms,
    audience: jwtVerifyAudience,
    clockTolerance: jwtVerifyClockTolerance,
    jwtId: jwtVerifyId,
    ignoreExpiration: jwtVerifyIgnoreExpiration,
    ignoreNotBefore: jwtVerifyIgnoreNotBefore,
    issuer: jwtVerifyIssuer,
    subject: jwtVerifySubject,
};
// Create’s our PostGraphQL server and provides all the appropriate
// configuration options.
var server = http_1.createServer(postgraphql_1.default(pgConfig, schemas, {
    classicIds: classicIds,
    dynamicJson: dynamicJson,
    disableDefaultMutations: disableDefaultMutations,
    graphqlRoute: graphqlRoute,
    graphiqlRoute: graphiqlRoute,
    graphiql: !disableGraphiql,
    jwtPgTypeIdentifier: jwtPgTypeIdentifier || deprecatedJwtPgTypeIdentifier,
    jwtSecret: jwtSecret || deprecatedJwtSecret,
    jwtAudiences: jwtAudiences,
    jwtRole: jwtRole,
    jwtVerifyOptions: jwtVerifyOptions,
    pgDefaultRole: pgDefaultRole,
    watchPg: watchPg,
    showErrorStack: showErrorStack,
    extendedErrors: extendedErrors,
    disableQueryLog: false,
    enableCors: enableCors,
    exportJsonSchemaPath: exportJsonSchemaPath,
    exportGqlSchemaPath: exportGqlSchemaPath,
    bodySizeLimit: bodySizeLimit,
    appendPlugins: loadPlugins(appendPluginNames),
    prependPlugins: loadPlugins(prependPluginNames),
    readCache: readCache,
    writeCache: writeCache,
}));
// Start our server by listening to a specific port and host name. Also log
// some instructions and other interesting information.
server.listen(port, hostname, function () {
    console.log('');
    console.log("PostGraphQL server listening on port " + chalk.underline(server.address().port.toString()) + " \uD83D\uDE80");
    console.log('');
    console.log("  \u2023 Connected to Postgres instance " + chalk.underline.blue(isDemo ? 'postgraphql_demo' : "postgres://" + pgConfig.host + ":" + (pgConfig.port || 5432) + (pgConfig.database != null ? "/" + pgConfig.database : '')));
    console.log("  \u2023 Introspected Postgres schema(s) " + schemas.map(function (schema) { return chalk.magenta(schema); }).join(', '));
    console.log("  \u2023 GraphQL endpoint served at " + chalk.underline("http://" + hostname + ":" + port + graphqlRoute));
    if (!disableGraphiql)
        console.log("  \u2023 GraphiQL endpoint served at " + chalk.underline("http://" + hostname + ":" + port + graphiqlRoute));
    console.log('');
    console.log(chalk.gray('* * *'));
    console.log('');
});
//# sourceMappingURL=data:application/json;base64, 