text: "use strict";
  // Copyright (c) Jupyter Development Team.
  // Distributed under the terms of the Modified BSD License.
  Object.defineProperty(exports, "__esModule", { value: true });
  const algorithm_1 = require("@lumino/algorithm");
  const polling_1 = require("@lumino/polling");
  const signaling_1 = require("@lumino/signaling");
  const __1 = require("..");
  const basemanager_1 = require("../basemanager");
  const restapi_1 = require("./restapi");
  const default_1 = require("./default");
  /**
   * A terminal session manager.
   */
  class TerminalManager extends basemanager_1.BaseManager {
      /**
       * Construct a new terminal manager.
       */
      constructor(options = {}) {
          var _a;
          super(options);
          this._isReady = false;
          // As an optimization, we unwrap the models to just store the names.
          this._names = [];
          this._terminalConnections = new Set();
          this._runningChanged = new signaling_1.Signal(this);
          this._connectionFailure = new signaling_1.Signal(this);
          // Check if terminals are available
          if (!this.isAvailable()) {
              this._ready = Promise.reject('Terminals unavailable');
              this._ready.catch(_ => undefined);
              return;
          }
          // Start polling with exponential backoff.
          this._pollModels = new polling_1.Poll({
              auto: false,
              factory: () => this.requestRunning(),
              frequency: {
                  interval: 10 * 1000,
                  backoff: true,
                  max: 300 * 1000
              },
              name: `@jupyterlab/services:TerminalManager#models`,
              standby: (_a = options.standby, (_a !== null && _a !== void 0 ? _a : 'when-hidden'))
          });
          // Initialize internal data.
          this._ready = (async () => {
              await this._pollModels.start();
              await this._pollModels.tick;
              this._isReady = true;
          })();
      }
      /**
       * Test whether the manager is ready.
       */
      get isReady() {
          return this._isReady;
      }
      /**
       * A promise that fulfills when the manager is ready.
       */
      get ready() {
          return this._ready;
      }
      /**
       * A signal emitted when the running terminals change.
       */
      get runningChanged() {
          return this._runningChanged;
      }
      /**
       * A signal emitted when there is a connection failure.
       */
      get connectionFailure() {
          return this._connectionFailure;
      }
      /**
       * Dispose of the resources used by the manager.
       */
      dispose() {
          if (this.isDisposed) {
              return;
          }
          this._names.length = 0;
          this._terminalConnections.forEach(x => x.dispose());
          this._pollModels.dispose();
          super.dispose();
      }
      /**
       * Whether the terminal service is available.
       */
      isAvailable() {
          return restapi_1.isAvailable();
      }
      /*
       * Connect to a running terminal.
       *
       * @param name - The name of the target terminal.
       *
       * @param options - The options used to connect to the terminal.
       *
       * @returns A promise that resolves to the new terminal connection instance.
       *
       * #### Notes
       * The manager `serverSettings` will be used.
       */
      connectTo(options) {
          const terminalConnection = new default_1.TerminalConnection(Object.assign(Object.assign({}, options), { serverSettings: this.serverSettings }));
          this._onStarted(terminalConnection);
          if (!this._names.includes(options.model.name)) {
              // We trust the user to connect to an existing session, but we verify
              // asynchronously.
              void this.refreshRunning().catch(() => {
                  /* no-op */
              });
          }
          return terminalConnection;
      }
      /**
       * Create an iterator over the most recent running terminals.
       *
       * @returns A new iterator over the running terminals.
       */
      running() {
          return algorithm_1.iter(this._models);
      }
      /**
       * Force a refresh of the running terminals.
       *
       * @returns A promise that with the list of running terminals.
       *
       * #### Notes
       * This is intended to be called only in response to a user action,
       * since the manager maintains its internal state.
       */
      async refreshRunning() {
          await this._pollModels.refresh();
          await this._pollModels.tick;
      }
      /**
       * Create a new terminal session.
       *
       * @returns A promise that resolves with the terminal instance.
       *
       * #### Notes
       * The manager `serverSettings` will be used unless overridden in the
       * options.
       */
      async startNew() {
          const model = await restapi_1.startNew(this.serverSettings);
          await this.refreshRunning();
          return this.connectTo({ model });
      }
      /**
       * Shut down a terminal session by name.
       */
      async shutdown(name) {
          await restapi_1.shutdownTerminal(name, this.serverSettings);
          await this.refreshRunning();
      }
      /**
       * Shut down all terminal sessions.
       *
       * @returns A promise that resolves when all of the sessions are shut down.
       */
      async shutdownAll() {
          // Update the list of models to make sure our list is current.
          await this.refreshRunning();
          // Shut down all models.
          await Promise.all(this._names.map(name => restapi_1.shutdownTerminal(name, this.serverSettings)));
          // Update the list of models to clear out our state.
          await this.refreshRunning();
      }
      /**
       * Execute a request to the server to poll running terminals and update state.
       */
      async requestRunning() {
          var _a;
          let models;
          try {
              models = await restapi_1.listRunning(this.serverSettings);
          }
          catch (err) {
              // Check for a network error, or a 503 error, which is returned
              // by a JupyterHub when a server is shut down.
              if (err instanceof __1.ServerConnection.NetworkError ||
                  ((_a = err.response) === null || _a === void 0 ? void 0 : _a.status) === 503) {
                  this._connectionFailure.emit(err);
              }
              throw err;
          }
          if (this.isDisposed) {
              return;
          }
          const names = models.map(({ name }) => name).sort();
          if (names === this._names) {
              // Identical models list, so just return
              return;
          }
          this._names = names;
          this._terminalConnections.forEach(tc => {
              if (!names.includes(tc.name)) {
                  tc.dispose();
              }
          });
          this._runningChanged.emit(this._models);
      }
      /**
       * Handle a session starting.
       */
      _onStarted(terminalConnection) {
          this._terminalConnections.add(terminalConnection);
          terminalConnection.disposed.connect(this._onDisposed, this);
      }
      /**
       * Handle a session terminating.
       */
      _onDisposed(terminalConnection) {
          this._terminalConnections.delete(terminalConnection);
          // Update the running models to make sure we reflect the server state
          void this.refreshRunning().catch(() => {
              /* no-op */
          });
      }
      get _models() {
          return this._names.map(name => {
              return { name };
          });
      }
  }
  exports.TerminalManager = TerminalManager;
  //# sourceMappingURL=manager.js.map