text: "use strict";
  var __extends = (this && this.__extends) || (function () {
      var extendStatics = Object.setPrototypeOf ||
          ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
          function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
      return function (d, b) {
          extendStatics(d, b);
          function __() { this.constructor = d; }
          d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
      };
  })();
  var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) {
      if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; }
      return cooked;
  };
  var __assign = (this && this.__assign) || Object.assign || function(t) {
      for (var s, i = 1, n = arguments.length; i < n; i++) {
          s = arguments[i];
          for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
              t[p] = s[p];
      }
      return t;
  };
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
      return new (P || (P = Promise))(function (resolve, reject) {
          function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
          function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
          function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
          step((generator = generator.apply(thisArg, _arguments || [])).next());
      });
  };
  var __generator = (this && this.__generator) || function (thisArg, body) {
      var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
      return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
      function verb(n) { return function (v) { return step([n, v]); }; }
      function step(op) {
          if (f) throw new TypeError("Generator is already executing.");
          while (_) try {
              if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
              if (y = 0, t) op = [0, t.value];
              switch (op[0]) {
                  case 0: case 1: t = op; break;
                  case 4: _.label++; return { value: op[1], done: false };
                  case 5: _.label++; y = op[1]; op = [0]; continue;
                  case 7: op = _.ops.pop(); _.trys.pop(); continue;
                  default:
                      if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                      if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                      if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                      if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                      if (t[2]) _.ops.pop();
                      _.trys.pop(); continue;
              }
              op = body.call(thisArg, _);
          } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
          if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
      }
  };
  Object.defineProperty(exports, "__esModule", { value: true });
  var events_1 = require("events");
  var http_1 = require("http");
  var launcher_1 = require("./launcher");
  // ApolloEngine is the main API used to run engineproxy. It integrates with your
  // Node web framework of choice.
  var ApolloEngine = /** @class */ (function (_super) {
      __extends(ApolloEngine, _super);
      // The constructor takes the underlying engineproxy config file. All options
      // specific to the Node API are passed to `listen` (or other entry point) to
      // maintain a strict separate between Node options and engineproxy config.
      function ApolloEngine(config) {
          if (config === void 0) { config = {}; }
          var _this = _super.call(this) || this;
          _this.config = config;
          _this.launcher = new launcher_1.ApolloEngineLauncher(config);
          _this.httpServer = null;
          return _this;
      }
      // listen tells your app to listen on an ephemeral port, then starts an
      // engineproxy listening on the specified port configured with your app's
      // ephemeral port as an origin.
      ApolloEngine.prototype.listen = function (options, listenCallback) {
          var _this = this;
          if (options.port === undefined && options.pipePath === undefined) {
              throw new Error('Must provide either the `pipePath` or the `port` that your app will be accessible on.');
          }
          var httpServer;
          var appsProvided = 0;
          if (options.httpServer) {
              httpServer = options.httpServer;
              appsProvided++;
          }
          if (options.expressApp) {
              httpServer = new http_1.Server(options.expressApp);
              appsProvided++;
          }
          if (options.connectApp) {
              httpServer = new http_1.Server(options.connectApp);
              appsProvided++;
          }
          if (options.koaApp) {
              httpServer = new http_1.Server(options.koaApp.callback());
              appsProvided++;
          }
          if (options.restifyServer) {
              httpServer = options.restifyServer.server;
              appsProvided++;
          }
          if (appsProvided === 0) {
              throw new Error('Must provide "httpServer", "expressApp", "connectApp", "koaApp", or "restifyServer"');
          }
          if (appsProvided > 1) {
              throw new Error('Must only provide one of "httpServer", "expressApp", "connectApp", "koaApp", and "restifyServer"');
          }
          this.httpServer = httpServer;
          // Note: if the listen fails, then httpServer will emit an error, and
          // there's no way for our user to catch it. However, this is exactly the
          // same situation as express/koa/connect's listen() method, so that's OK; if
          // the user wants to listen for that error they can spend one line turning
          // their app into an http.Server and pass that in instead.
          // (And with restify, they have access to restifyServer.server themselves.)
          this.httpServer.listen({ port: 0, host: options.innerHost || '127.0.0.1' }, function () {
              // The Node server is now listening, so we can figure out what its address
              // is!
              //
              // We run listenCallback and this.emit('error') outside of this Promise's
              // then/catch, because we want to avoid making `listen` a Promisey API
              // (because we want it to work like httpServer.listen), and doing stuff
              // that can throw in a then/catch means that we would need somebody to be
              // catch-ing the Promise itself.
              _this.startEngine(httpServer.address(), options)
                  .then(function () { return listenCallback && process.nextTick(listenCallback); })
                  .catch(function (error) {
                  process.nextTick(function () { return _this.emit('error', error); });
              });
          });
      };
      // Stops Engine and your app.
      ApolloEngine.prototype.stop = function () {
          return __awaiter(this, void 0, void 0, function () {
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0: return [4 /*yield*/, this.launcher.stop()];
                      case 1:
                          _a.sent();
                          // XXX Should we also wait for all current connections to be closed?
                          this.httpServer.close();
                          this.httpServer = null;
                          return [2 /*return*/];
                  }
              });
          });
      };
      // Call this from the top level of a Meteor server as
      // `engine.meteorListen(WebApp)` to hook in to the built-in connect server.
      ApolloEngine.prototype.meteorListen = function (webApp, options) {
          var _this = this;
          if (options === void 0) { options = {}; }
          var makeListenPolyfill = function (httpServer) { return function (listenOptions, cb) {
              var engineListenOptions;
              if (listenOptions.path !== undefined) {
                  throw Error('Engine does not support listening on a path');
              }
              if (listenOptions.port === undefined) {
                  if (!listenOptions.pipePath) {
                      throw Error('Engine needs a port or a pipe name to listen on');
                  }
                  engineListenOptions = {
                      pipePath: listenOptions.pipePath,
                  };
              }
              else {
                  engineListenOptions = {
                      port: listenOptions.port,
                      host: listenOptions.host,
                  };
              }
              _this.listen(__assign({}, options, engineListenOptions, { httpServer: httpServer }), cb);
          }; };
          // Try to use an API to be added in Meteor 1.6.2 that lets us override the
          // built-in listen call.
          if (webApp.startListening) {
              webApp.startListening = function (httpServer, listenOptions, cb) {
                  makeListenPolyfill(httpServer)(listenOptions, cb);
              };
              return;
          }
          // Hacky pre-1.6.2 approach.
          var originalListen = webApp.httpServer.listen;
          var listenPolyfill = makeListenPolyfill(webApp.httpServer);
          webApp.httpServer.listen = function (listenOptions, cb) {
              webApp.httpServer.listen = originalListen;
              listenPolyfill(listenOptions, cb);
          };
      };
      // Pass this as `listener` to hapi.Server's constructor along with
      // `autoListen: false` to hopefully integrate with hapi.
      ApolloEngine.prototype.hapiListener = function (options) {
          return __awaiter(this, void 0, void 0, function () {
              var _this = this;
              // The autoListen:false feature of hapi is semi-broken: some key
              // functionality depends on the 'listening' event being evoked even if you
              // told it it's already listening. Here's a fun hack to make sure we call it
              // anyway!
              function callListeningImmediately(event, listening) {
                  if (event !== 'listening') {
                      return;
                  }
                  httpServer.removeListener('newListener', callListeningImmediately);
                  process.nextTick(function () { return httpServer.emit('listening'); });
              }
              var httpServer, p;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          httpServer = new http_1.Server();
                          p = new Promise(function (resolve, reject) {
                              _this.once('error', reject);
                              _this.listen(__assign({}, options, { httpServer: httpServer }), resolve);
                          });
                          return [4 /*yield*/, p];
                      case 1:
                          _a.sent();
                          httpServer.on('newListener', callListeningImmediately);
                          return [2 /*return*/, httpServer];
                  }
              });
          });
      };
      ApolloEngine.prototype.startEngine = function (innerAddress, options) {
          return __awaiter(this, void 0, void 0, function () {
              var stringPort, winPipePrefix, defaults, launcherOptions, _a;
              return __generator(this, function (_b) {
                  switch (_b.label) {
                      case 0:
                          if (options.port === undefined && options.pipePath === undefined) {
                              throw new Error('Either `port` or `pipePath` must be defined');
                          }
                          else if (typeof options.port === 'string') {
                              stringPort = options.port;
                              options.port = parseInt(stringPort, 10);
                              if (isNaN(options.port)) {
                                  winPipePrefix = String.raw(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\\.pipe"], ["\\\\.\\pipe"])));
                                  if (stringPort.startsWith(winPipePrefix + '\\')) {
                                      options.pipePath = stringPort;
                                      options.port = undefined;
                                  }
                                  else {
                                      throw new Error('port must be an integer or a Windows named pipe, ' +
                                          ("not '" + stringPort + "'"));
                                  }
                              }
                          }
                          if (options.pipePath !== undefined && options.port !== undefined) {
                              throw new Error('Only one of `port `and `pipePath` may be set');
                          }
                          defaults = {
                              frontendHost: options.host,
                              frontendPort: options.port || undefined,
                              frontendPipePath: options.pipePath || undefined,
                              graphqlPaths: options.graphqlPaths || ['/graphql'],
                              originUrl: "http://" + launcher_1.joinHostPort(innerAddress.address, innerAddress.port),
                              // Support multiple graphqlPaths.
                              useFrontendPathForDefaultOrigin: true,
                          };
                          launcherOptions = Object.assign({}, options.launcherOptions);
                          launcherOptions.extraArgs = (launcherOptions.extraArgs || []).concat([
                              "-defaults=" + JSON.stringify(defaults),
                          ]);
                          _a = this;
                          return [4 /*yield*/, this.launcher.start(launcherOptions)];
                      case 1:
                          _a.engineListeningAddress = _b.sent();
                          return [2 /*return*/];
                  }
              });
          });
      };
      return ApolloEngine;
  }(events_1.EventEmitter));
  exports.ApolloEngine = ApolloEngine;
  var templateObject_1;
  //# sourceMappingURL=engine.js.map