export default [
  'application/json',
  'application/xml',
  'application/x-www-form-urlencoded',
  'multipart/form-data',
  'multipart/byteranges',
  'application/octet-stream',
  'text/plain',
  'application/javascript',
  'application/pdf',
  'text/html',
  'image/png',
  'image/jpeg',
  'image/gif',
  'image/webp',
  'text/css',
  'application/x-pkcs12',
  'application/xhtml+xml',
  'application/andrew-inset',
  'application/applixware',
  'application/atom+xml',
  'application/atomcat+xml',
  'application/atomsvc+xml',
  'application/bdoc',
  'application/cu-seeme',
  'application/davmount+xml',
  'application/docbook+xml',
  'application/dssc+xml',
  'application/ecmascript',
  'application/epub+zip',
  'application/exi',
  'application/font-tdpfr',
  'application/font-woff',
  'application/font-woff2',
  'application/geo+json',
  'application/graphql',
  'application/java-serialized-object',
  'application/json5',
  'application/jsonml+json',
  'application/ld+json',
  'application/lost+xml',
  'application/manifest+json',
  'application/mp4',
  'application/msword',
  'application/mxf',
  'application/oda',
  'application/ogg',
  'application/pgp-encrypted',
  'application/pgp-signature',
  'application/pics-rules',
  'application/pkcs10',
  'application/pkcs7-mime',
  'application/pkcs7-signature',
  'application/pkcs8',
  'application/postscript',
  'application/pskc+xml',
  'application/resource-lists+xml',
  'application/resource-lists-diff+xml',
  'application/rls-services+xml',
  'application/rsd+xml',
  'application/rss+xml',
  'application/rtf',
  'application/sdp',
  'application/shf+xml',
  'application/timestamped-data',
  'application/vnd.android.package-archive',
  'application/vnd.api+json',
  'application/vnd.apple.installer+xml',
  'application/vnd.apple.mpegurl',
  'application/vnd.apple.pkpass',
  'application/vnd.bmi',
  'application/vnd.curl.car',
  'application/vnd.curl.pcurl',
  'application/vnd.dna',
  'application/vnd.google-apps.document',
  'application/vnd.google-apps.presentation',
  'application/vnd.google-apps.spreadsheet',
  'application/vnd.hal+xml',
  'application/vnd.handheld-entertainment+xml',
  'application/vnd.macports.portpkg',
  'application/vnd.unity',
  'application/vnd.zul',
  'application/widget',
  'application/wsdl+xml',
  'application/x-7z-compressed',
  'application/x-ace-compressed',
  'application/x-bittorrent',
  'application/x-bzip',
  'application/x-bzip2',
  'application/x-cfs-compressed',
  'application/x-chrome-extension',
  'application/x-cocoa',
  'application/x-envoy',
  'application/x-eva',
  'font/opentype',
  'application/x-gca-compressed',
  'application/x-gtar',
  'application/x-hdf',
  'application/x-httpd-php',
  'application/x-install-instructions',
  'application/x-latex',
  'application/x-lua-bytecode',
  'application/x-lzh-compressed',
  'application/x-ms-application',
  'application/x-ms-shortcut',
  'application/x-ndjson',
  'application/x-perl',
  'application/x-pkcs7-certificates',
  'application/x-pkcs7-certreqresp',
  'application/x-rar-compressed',
  'application/x-sh',
  'application/x-sql',
  'application/x-subrip',
  'application/x-t3vm-image',
  'application/x-tads',
  'application/x-tar',
  'application/x-tcl',
  'application/x-tex',
  'application/x-x509-ca-cert',
  'application/xop+xml',
  'application/xslt+xml',
  'application/zip',
  'audio/3gpp',
  'audio/adpcm',
  'audio/basic',
  'audio/midi',
  'audio/mpeg',
  'audio/mp4',
  'audio/ogg',
  'audio/silk',
  'audio/wave',
  'audio/webm',
  'audio/x-aac',
  'audio/x-aiff',
  'audio/x-caf',
  'audio/x-flac',
  'audio/xm',
  'image/bmp',
  'image/cgm',
  'image/sgi',
  'image/svg+xml',
  'image/tiff',
  'image/x-3ds',
  'image/x-freehand',
  'image/x-icon',
  'image/x-jng',
  'image/x-mrsid-image',
  'image/x-pcx',
  'image/x-pict',
  'image/x-rgb',
  'image/x-tga',
  'message/rfc822',
  'text/cache-manifest',
  'text/calendar',
  'text/coffeescript',
  'text/csv',
  'text/hjson',
  'text/jade',
  'text/jsx',
  'text/less',
  'text/mathml',
  'text/n3',
  'text/richtext',
  'text/sgml',
  'text/slim',
  'text/stylus',
  'text/tab-separated-values',
  'text/uri-list',
  'text/vcard',
  'text/vnd.curl',
  'text/vnd.fly',
  'text/vtt',
  'text/x-asm',
  'text/x-c',
  'text/x-component',
  'text/x-fortran',
  'text/x-handlebars-template',
  'text/x-java-source',
  'text/x-lua',
  'text/x-markdown',
  'text/x-nfo',
  'text/x-opml',
  'text/x-pascal',
  'text/x-processing',
  'text/x-sass',
  'text/x-scss',
  'text/x-vcalendar',
  'text/xml',
  'text/yaml',
  'video/3gpp',
  'video/3gpp2',
  'video/h261',
  'video/h263',
  'video/h264',
  'video/jpeg',
  'video/jpm',
  'video/mj2',
  'video/mp2t',
  'video/mp4',
  'video/mpeg',
  'video/ogg',
  'video/quicktime',
  'video/webm',
  'video/x-f4v',
  'video/x-fli',
  'video/x-flv',
  'video/x-m4v',
];


},
"main": "index.js",
"insomnia": {
"name": "core-themes"
},
"scripts": {
"test": "node --version"

"name": "core-themes",
"core": true,
"unlisted": true


keep keep keep keep keep replace keep keep keep keep

"name": "file",
"core": true,
"unlisted": true

insomnia-plugin-response

Insomnia response template tag


import React, { PureComponent } from 'react';
import autobind from 'autobind-decorator';
import Modal from '../base/modal';
import ModalBody from '../base/modal-body';
import ModalHeader from '../base/modal-header';
import ModalFooter from '../base/modal-footer';

@autobind
class AlertModal extends PureComponent {
  constructor(props) {
    super(props);

    this.state = {
      title: '',
      message: '',
      addCancel: false,
      okLabel: '',
    };
  }

  _setModalRef(m) {
    this.modal = m;
  }

  _handleOk() {
    this.hide();
    this._okCallback();

    if (typeof this._okCallback2 === 'function') {
      this._okCallback2();
    }
  }

  hide() {
    this.modal.hide();
  }

  setCancelRef(n) {
    this._cancel = n;
  }

  setOkRef(n) {
    this._ok = n;
  }

  show(options = {}) {
    const { title, message, addCancel, onConfirm, okLabel } = options;
    this.setState({ title, message, addCancel, okLabel });

    this.modal && this.modal.show();

    // Need to do this after render because modal focuses itself too
    setTimeout(() => {
      this._cancel && this._cancel.focus();
    }, 100);

    this._okCallback2 = onConfirm;

    return new Promise(resolve => {
      this._okCallback = resolve;
    });
  }

  render() {
    const { message, title, addCancel, okLabel } = this.state;

    return (
      <Modal ref={this._setModalRef} closeOnKeyCodes={[13]} skinny>
        <ModalHeader>{title || 'Uh Oh!'}</ModalHeader>
        <ModalBody className="wide pad">{message}</ModalBody>
        <ModalFooter>
          <div>
            {addCancel ? (
              <button className="btn" ref={this.setCancelRef} onClick={this.hide}>
                Cancel
              </button>
            ) : null}
            <button className="btn" ref={this.setOkRef} onClick={this._handleOk}>
              {okLabel || 'Ok'}
            </button>
          </div>
        </ModalFooter>
      </Modal>
    );
  }
}

export default AlertModal;


// @flow
import * as React from 'react';
import styled from 'styled-components';

type Props = {|
  className?: string,
  gridLeft?: React.Node,
  gridCenter?: React.Node,
  gridRight?: React.Node,
|};

const StyledHeader: React.ComponentType<{}> = styled.div`
      border-bottom:1px solid var(--hl-md);
      padding: var(--padding-sm) var(--padding-md);
      display: grid;
      grid-template-columns: 2fr 1.5fr 2fr;
      grid-template-rows: 1fr;
      grid-template-areas: "header_left header_center header_right";
      .header_left {
        grid-area: header_left;
        text-align: left;
        display:flex;
        align-items: center;
      }
      .header_center {
        grid-area: header_center;
        text-align: center;
        display:flex;
        align-items: center;
      }
      .header_right {
        grid-area: header_right;
        text-align: right;
        display:flex;
        align-items: center;
        justify-content: flex-end;
      }
    `;

class Header extends React.PureComponent<Props> {
  render() {
    const { className, gridLeft, gridCenter, gridRight } = this.props;
    return (

    );
  }
}

export default Header;


// @flow
import * as React from 'react';
import { Table, TableBody, TableData, TableHead, TableHeader, TableRow } from './table';
import Button from './button';
import styled from 'styled-components';
import SvgIcon, { IconEnum } from './svg-icon';

type Notice = {|
  type: 'warning' | 'error',
  line: number,
  message: string,
|};

type Props = {
  notices: Array<Notice>,

  // Optional
  onClick?: (n: Notice, e: SyntheticEvent<HTMLElement>) => any,
  onVisibilityToggle?: (expanded: boolean) => any,
  compact?: boolean,
  className?: string,
};

type State = {
  collapsed: boolean,
};

const Wrapper: React.ComponentType<{}> = styled.div`
  width: 100%;

  td {
    position: relative;
  }

  tr:hover {
    background-color: var(--hl-sm) !important;
  }
`;

const ScrollWrapperStyled: React.ComponentType<{}> = styled.div`
  height: 100%;
  width: 100%;
  max-height: 13rem;
  overflow-y: auto;
`;

const ErrorCount: React.ComponentType<{}> = styled.div`
  margin-right: var(--padding-md);
  display: inline-flex;
  align-items: center;
`;

const JumpButton: React.ComponentType<{}> = styled.button`
  outline: 0;
  border: 0;
  background: transparent;
  font-size: var(--font-size-md);
  margin: 0;
  display: none;

  position: absolute;
  top: 0;
  bottom: 0;
  right: -0.5em;
  padding-left: 0.5em;
  padding-right: 0.5em;
  z-index: 1;

  & svg {
    opacity: 0.7;
  }

  &:hover svg {
    opacity: 1;
  }

  &:active svg {
    opacity: 0.85;
  }

  tr:hover & {
    // To keep icon centered vertically
    display: flex;
    align-items: center;
  }
`;

const Header: React.ComponentType<{}> = styled.header`
  display: flex;
  align-items: center;
  flex-grow: 0;
  justify-content: space-between;
  border: 1px solid var(--hl-sm);
  border-left: 0;
  border-right: 0;
  padding-left: var(--padding-md);
`;

class NoticeTable extends React.PureComponent<Props, State> {
  state = {
    collapsed: false,
  };

  collapse(e: SyntheticEvent<HTMLButtonElement>) {
    const { onVisibilityToggle } = this.props;
    this.setState(
      state => ({ collapsed: !state.collapsed }),
      () => {
        if (onVisibilityToggle) {
          onVisibilityToggle(!this.state.collapsed);
        }
      },
    );
  }

  onClick(notice: Notice, e: SyntheticEvent<HTMLButtonElement>) {
    const { onClick } = this.props;
    if (!onClick) {
      return;
    }

    onClick(notice, e);
  }

  render() {
    const { notices, compact } = this.props;
    const { collapsed } = this.state;

    const caret = collapsed
      ? <SvgIcon icon={IconEnum.chevronUp} />
      : <SvgIcon icon={IconEnum.chevronDown} />;

    const errors = notices.filter(n => n.type === 'error');
    const warnings = notices.filter(n => n.type === 'warning');

    return (
      <Wrapper>
        <Header>
          <div>
            {errors.length > 0 && (
              <ErrorCount>
                <SvgIcon icon={IconEnum.error} label={errors.length} />
              </ErrorCount>
            )}
            {warnings.length > 0 && (
              <ErrorCount>
                <SvgIcon icon={IconEnum.warning} label={warnings.length} />
              </ErrorCount>
            )}
          </div>
          <Button onClick={this.collapse.bind(this)} noOutline>
            {collapsed ? 'Show' : 'Hide'} Details{caret}
          </Button>
        </Header>
        {!collapsed && (
          <ScrollWrapperStyled>
            <Table striped compact={compact}>
              <TableHead>
                <TableRow>
                  <TableHeader align="center">Type</TableHeader>
                  <TableHeader style={{ minWidth: '3em' }} align="center">
                    Line
                  </TableHeader>
                  <TableHeader style={{ width: '100%' }} align="left">
                    Message
                  </TableHeader>
                </TableRow>
              </TableHead>
              <TableBody>
                {notices.map((n, i) => (
                  <TableRow key={`${n.line}:${n.type}:${n.message}`}>
                    <TableData align="center">
                      <SvgIcon icon={n.type} />
                    </TableData>
                    <TableData align="center">
                      {n.line}
                      <JumpButton onClick={this.onClick.bind(this, n)}>
                        <SvgIcon icon={IconEnum.arrowRight} />
                      </JumpButton>
                    </TableData>
                    <TableData align="left">{n.message}</TableData>
                  </TableRow>
                ))}
              </TableBody>
            </Table>
          </ScrollWrapperStyled>
        )}
      </Wrapper>
    );
  }
}

export default NoticeTable;


keep keep keep keep keep replace keep keep keep keep keep

insomnia-plugin-cookie-jar

Insomnia cookie jar template tag


.graphql-explorer {
  display: grid;
  grid-template-columns: 100%;
  grid-template-rows: auto minmax(0, 1fr);
  position: fixed;
  right: 0;
  top: 0;
  bottom: 0;
  width: 400px;
  max-width: 100%;
  z-index: 100;
  background: var(--color-bg);
  box-shadow: 0 0 1rem 0 rgba(0, 0, 0, 0.4);
  color: var(--color-font);

  .markdown-preview__content *:first-child {
    margin-top: var(--padding-sm);
  }

  &__subheading {
    border-bottom: 1px solid var(--hl-md);
    padding-bottom: var(--padding-sm);
    margin-top: var(--padding-lg);
    margin-bottom: var(--padding-md);
  }

  &__header {
    overflow: hidden;
    border-bottom: 1px solid var(--hl-md);
    box-sizing: border-box;
    height: var(--height-nav);
    line-height: var(--line-height-md);
    color: var(--color-font);
    display: flex;
    justify-content: space-between;
    flex-direction: row;

    h1 {
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      font-size: var(--font-size-lg);
      font-weight: bold;
    }

    & > * {
      white-space: nowrap;
      padding-left: var(--padding-md);
      display: inline-block;
      margin: 0;
    }

    &__close-btn {
      height: 100% !important;
    }
  }

  &__body {
    box-sizing: border-box;
    padding: var(--padding-md) var(--padding-md) 10rem;
    overflow: auto;
    height: 100%;
    max-height: 100%;
  }

  &__defs {
    &__description {
      margin-left: var(--padding-sm);
      color: var(--color-font);
    }

    &__arg {
      margin-left: var(--padding-md);
    }

    li {
      margin-bottom: var(--padding-md);
    }
  }

  a {
    font-weight: normal !important;
  }
}


# Insomnia App

The main desktop application.


// @flow

type SignatureMethod = 'HMAC-SHA1' | 'RSA-SHA1' | 'PLAINTEXT';
type Token = { key: string, secret?: string };
type RequestData = {
  url: string,
  method: string,
  data?: { [string]: string },
};

type OAuth1Config = {
  consumer: {
    key: string,
    secret: string,
  },
  signature_method: 'HMAC-SHA1' | 'RSA-SHA1' | 'PLAINTEXT',
  version: '1.0',
  hash_function: (signatureMethod: SignatureMethod, key: string) => string,
};

declare class OAuth1 {
  constructor(config: OAuth1Config): OAuth1;
  authorize: (data: RequestData, token: Token | null) => RequestData;
  toHeader: (data: RequestData) => { Authorization: string };
  getSigningKey: (tokenSecret: string) => string;
}

declare module 'oauth-1.0a' {
  declare module.exports: typeof OAuth1;
}


"description": "Template tag to pull data out of JSON strings",

Insomnia cookie jar template tag


// @flow

import { send } from '../../network/network';
import type { Request } from '../../models/request';
import * as models from '../../models';
import type { ExtraRenderInfo } from '../../common/render';

export function init(activeEnvironmentId: string | null): { network: Object } {
  const network = {
    async sendRequest(request: Request, extraInfo?: ExtraRenderInfo): Promise<Response> {
      const responsePatch = await send(request._id, activeEnvironmentId || undefined, extraInfo);
      const settings = await models.settings.getOrCreate();
      return models.response.create(responsePatch, settings.maxHistoryResponses);
    },
  };

  return { network };
}


import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import autobind from 'autobind-decorator';
import fs from 'fs';
import { clipboard, ipcRenderer, remote } from 'electron';
import { parse as urlParse } from 'url';
import HTTPSnippet from 'httpsnippet';
import ReactDOM from 'react-dom';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import Wrapper from '../components/wrapper';
import WorkspaceEnvironmentsEditModal from '../components/modals/workspace-environments-edit-modal';
import Toast from '../components/toast';
import CookiesModal from '../components/modals/cookies-modal';
import RequestSwitcherModal from '../components/modals/request-switcher-modal';
import SettingsModal, { TAB_INDEX_SHORTCUTS } from '../components/modals/settings-modal';
import { ACTIVITY_HOME, ACTIVITY_INSOMNIA } from '../components/activity-bar/activity-bar';
import {
  COLLAPSE_SIDEBAR_REMS,
  DEFAULT_PANE_HEIGHT,
  DEFAULT_PANE_WIDTH,
  DEFAULT_SIDEBAR_WIDTH,
  MAX_PANE_HEIGHT,
  MAX_PANE_WIDTH,
  MAX_SIDEBAR_REMS,
  MIN_PANE_HEIGHT,
  MIN_PANE_WIDTH,
  MIN_SIDEBAR_REMS,
  PREVIEW_MODE_SOURCE,
  getAppName,
} from '../../common/constants';
import * as globalActions from '../redux/modules/global';
import * as entitiesActions from '../redux/modules/entities';
import * as db from '../../common/database';
import * as models from '../../models';
import {
  selectActiveCookieJar,
  selectActiveGitRepository,
  selectActiveOAuth2Token,
  selectActiveRequest,
  selectActiveRequestMeta,
  selectActiveRequestResponses,
  selectActiveResponse,
  selectActiveWorkspace,
  selectActiveWorkspaceClientCertificates,
  selectActiveWorkspaceMeta,
  selectEntitiesLists,
  selectSidebarChildren,
  selectSyncItems,
  selectUnseenWorkspaces,
  selectWorkspaceRequestsAndRequestGroups,
} from '../redux/selectors';
import RequestCreateModal from '../components/modals/request-create-modal';
import GenerateCodeModal from '../components/modals/generate-code-modal';
import WorkspaceSettingsModal from '../components/modals/workspace-settings-modal';
import RequestSettingsModal from '../components/modals/request-settings-modal';
import RequestRenderErrorModal from '../components/modals/request-render-error-modal';
import * as network from '../../network/network';
import { debounce, getContentDispositionHeader, getDataDirectory } from '../../common/misc';
import * as mime from 'mime-types';
import * as path from 'path';
import * as render from '../../common/render';
import { getKeys } from '../../templating/utils';
import { showAlert, showModal, showPrompt } from '../components/modals/index';
import { exportHarRequest } from '../../common/har';
import { hotKeyRefs } from '../../common/hotkeys';
import { executeHotKey } from '../../common/hotkeys-listener';
import KeydownBinder from '../components/keydown-binder';
import ErrorBoundary from '../components/error-boundary';
import * as plugins from '../../plugins';
import * as templating from '../../templating/index';
import AskModal from '../components/modals/ask-modal';
import { updateMimeType } from '../../models/request';
import MoveRequestGroupModal from '../components/modals/move-request-group-modal';
import * as themes from '../../plugins/misc';
import ExportRequestsModal from '../components/modals/export-requests-modal';
import FileSystemDriver from '../../sync/store/drivers/file-system-driver';
import VCS from '../../sync/vcs';
import SyncMergeModal from '../components/modals/sync-merge-modal';
import GitVCS, { GIT_NAMESPACE_DIR } from '../../sync/git/git-vcs';
import NeDBPlugin from '../../sync/git/ne-db-plugin';
import FSPlugin from '../../sync/git/fs-plugin';
import { routableFSPlugin } from '../../sync/git/routable-fs-plugin';
import AppContext from '../../common/strings';

@autobind
class App extends PureComponent {
  constructor(props) {
    super(props);

    this.state = {
      showDragOverlay: false,
      draggingSidebar: false,
      draggingPaneHorizontal: false,
      draggingPaneVertical: false,
      sidebarWidth: props.sidebarWidth || DEFAULT_SIDEBAR_WIDTH,
      paneWidth: props.paneWidth || DEFAULT_PANE_WIDTH,
      paneHeight: props.paneHeight || DEFAULT_PANE_HEIGHT,
      isVariableUncovered: props.isVariableUncovered || false,
      vcs: null,
      gitVCS: null,
      forceRefreshCounter: 0,
      forceRefreshHeaderCounter: 0,
      isMigratingChildren: false,
    };

    this._getRenderContextPromiseCache = {};

    this._savePaneWidth = debounce(paneWidth => this._updateActiveWorkspaceMeta({ paneWidth }));
    this._savePaneHeight = debounce(paneHeight => this._updateActiveWorkspaceMeta({ paneHeight }));
    this._saveSidebarWidth = debounce(sidebarWidth =>
      this._updateActiveWorkspaceMeta({ sidebarWidth }),
    );

    this._globalKeyMap = null;
  }

  _setGlobalKeyMap() {
    this._globalKeyMap = [
      [
        hotKeyRefs.PREFERENCES_SHOW_GENERAL,
        () => {
          App._handleShowSettingsModal();
        },
      ],
      [
        hotKeyRefs.PREFERENCES_SHOW_KEYBOARD_SHORTCUTS,
        () => {
          App._handleShowSettingsModal(TAB_INDEX_SHORTCUTS);
        },
      ],
      [
        hotKeyRefs.SHOW_RECENT_REQUESTS,
        () => {
          showModal(RequestSwitcherModal, {
            disableInput: true,
            maxRequests: 10,
            maxWorkspaces: 0,
            selectOnKeyup: true,
            title: 'Recent Requests',
            hideNeverActiveRequests: true,

            // Add an open delay so the dialog won't show for quick presses
            openDelay: 150,
          });
        },
      ],
      [
        hotKeyRefs.WORKSPACE_SHOW_SETTINGS,
        () => {
          const { activeWorkspace } = this.props;
          showModal(WorkspaceSettingsModal, activeWorkspace);
        },
      ],
      [
        hotKeyRefs.REQUEST_SHOW_SETTINGS,
        () => {
          if (this.props.activeRequest) {
            showModal(RequestSettingsModal, {
              request: this.props.activeRequest,
            });
          }
        },
      ],
      [
        hotKeyRefs.REQUEST_QUICK_SWITCH,
        () => {
          showModal(RequestSwitcherModal);
        },
      ],
      [hotKeyRefs.REQUEST_SEND, this._handleSendShortcut],
      [
        hotKeyRefs.ENVIRONMENT_SHOW_EDITOR,
        () => {
          const { activeWorkspace } = this.props;
          showModal(WorkspaceEnvironmentsEditModal, activeWorkspace);
        },
      ],
      [
        hotKeyRefs.SHOW_COOKIES_EDITOR,
        () => {
          const { activeWorkspace } = this.props;
          showModal(CookiesModal, activeWorkspace);
        },
      ],
      [
        hotKeyRefs.REQUEST_QUICK_CREATE,
        async () => {
          const { activeRequest, activeWorkspace } = this.props;
          const parentId = activeRequest ? activeRequest.parentId : activeWorkspace._id;
          const request = await models.request.create({ parentId, name: 'New Request' });
          await this._handleSetActiveRequest(request._id);
        },
      ],
      [
        hotKeyRefs.REQUEST_SHOW_CREATE,
        () => {
          const { activeRequest, activeWorkspace } = this.props;
          const parentId = activeRequest ? activeRequest.parentId : activeWorkspace._id;
          this._requestCreate(parentId);
        },
      ],
      [
        hotKeyRefs.REQUEST_SHOW_DELETE,
        () => {
          const { activeRequest } = this.props;

          if (!activeRequest) {
            return;
          }

          showModal(AskModal, {
            title: 'Delete Request?',
            message: `Really delete ${activeRequest.name}?`,
            onDone: confirmed => {
              if (!confirmed) {
                return;
              }
              models.request.remove(activeRequest);
            },
          });
        },
      ],
      [
        hotKeyRefs.REQUEST_SHOW_CREATE_FOLDER,
        () => {
          const { activeRequest, activeWorkspace } = this.props;
          const parentId = activeRequest ? activeRequest.parentId : activeWorkspace._id;
          this._requestGroupCreate(parentId);
        },
      ],
      [
        hotKeyRefs.REQUEST_SHOW_GENERATE_CODE_EDITOR,
        async () => {
          showModal(GenerateCodeModal, this.props.activeRequest);
        },
      ],
      [
        hotKeyRefs.REQUEST_SHOW_DUPLICATE,
        async () => {
          await this._requestDuplicate(this.props.activeRequest);
        },
      ],
      [
        hotKeyRefs.REQUEST_TOGGLE_PIN,
        async () => {
          if (!this.props.activeRequest) {
            return;
          }

          const metas = Object.values(this.props.entities.requestMetas).find(
            m => m.parentId === this.props.activeRequest._id,
          );

          await this._handleSetRequestPinned(this.props.activeRequest, !(metas && metas.pinned));
        },
      ],
      [hotKeyRefs.PLUGIN_RELOAD, this._handleReloadPlugins],
      [
        hotKeyRefs.ENVIRONMENT_UNCOVER_VARIABLES,
        async () => {
          await this._updateIsVariableUncovered();
        },
      ],
      [
        hotKeyRefs.SIDEBAR_TOGGLE,
        () => {
          this._handleToggleSidebar();
        },
      ],
    ];
  }

  async _handleSendShortcut() {
    const { activeRequest, activeEnvironment } = this.props;
    await this._handleSendRequestWithEnvironment(
      activeRequest ? activeRequest._id : 'n/a',
      activeEnvironment ? activeEnvironment._id : 'n/a',
    );
  }

  _setRequestPaneRef(n) {
    this._requestPane = n;
  }

  _setResponsePaneRef(n) {
    this._responsePane = n;
  }

  _setSidebarRef(n) {
    this._sidebar = n;
  }

  _requestGroupCreate(parentId) {
    showPrompt({
      title: 'New Folder',
      defaultValue: 'My Folder',
      submitName: 'Create',
      label: 'Name',
      selectText: true,
      onComplete: async name => {
        const requestGroup = await models.requestGroup.create({
          parentId,
          name,
        });
        await models.requestGroupMeta.create({
          parentId: requestGroup._id,
          collapsed: false,
        });
      },
    });
  }

  _requestCreate(parentId) {
    showModal(RequestCreateModal, {
      parentId,
      onComplete: request => {
        this._handleSetActiveRequest(request._id);
      },
    });
  }

  static async _requestGroupDuplicate(requestGroup) {
    showPrompt({
      title: 'Duplicate Folder',
      defaultValue: requestGroup.name,
      submitName: 'Create',
      label: 'New Name',
      selectText: true,
      onComplete: async name => {
        await models.requestGroup.duplicate(requestGroup, { name });
      },
    });
  }

  static async _requestGroupMove(requestGroup) {
    showModal(MoveRequestGroupModal, { requestGroup });
  }

  _requestDuplicate(request) {
    if (!request) {
      return;
    }

    showPrompt({
      title: 'Duplicate Request',
      defaultValue: request.name,
      submitName: 'Create',
      label: 'New Name',
      selectText: true,
      onComplete: async name => {
        const newRequest = await models.request.duplicate(request, { name });
        await this._handleSetActiveRequest(newRequest._id);
      },
    });
  }

  _workspaceRename(callback, workspaceId) {
    const workspace = this.props.workspaces.find(w => w._id === workspaceId);
    console.dir(AppContext);
    showPrompt({
      title: `Rename ${AppContext.workspace}`,
      defaultValue: workspace.name,
      submitName: 'Rename',
      selectText: true,
      label: 'Name',
      onComplete: async name => {
        await models.workspace.update(workspace, { name: name });
        callback();
      },
    });
  }

  _workspaceDeleteById(callback, workspaceId) {
    const workspace = this.props.workspaces.find(w => w._id === workspaceId);
    showModal(AskModal, {
      title: `Delete ${AppContext.workspace}`,
      message: `Do you really want to delete ${workspace.name}?`,
      yesText: 'Yes',
      noText: 'Cancel',
      onDone: async isYes => {
        if (!isYes) {
          return;
        }
        await models.workspace.remove(workspace);
      },
    });
  }

  _workspaceDuplicateById(callback, workspaceId) {
    const workspace = this.props.workspaces.find(w => w._id === workspaceId);

    showPrompt({
      title: `Duplicate ${AppContext.workspace}`,
      defaultValue: workspace.name,
      submitName: 'Create',
      selectText: true,
      label: 'New Name',
      onComplete: async name => {
        const newWorkspace = await db.duplicate(workspace, { name });
        await this.props.handleSetActiveWorkspace(newWorkspace._id);
        callback();
      },
    });
  }

  _workspaceDuplicate(callback) {
    this._workspaceDuplicateById(callback, this.props.activeWorkspace._id);
  }

  async _fetchRenderContext() {
    const { activeEnvironment, activeRequest, activeWorkspace } = this.props;
    const environmentId = activeEnvironment ? activeEnvironment._id : null;

    const ancestors = await db.withAncestors(activeRequest || activeWorkspace, [
      models.request.type,
      models.requestGroup.type,
      models.workspace.type,
    ]);

    return render.getRenderContext(activeRequest, environmentId, ancestors);
  }

  async _handleGetRenderContext() {
    const context = await this._fetchRenderContext();
    const keys = getKeys(context);
    return { context, keys };
  }

  /**
   * Heavily optimized render function
   *
   * @param text - template to render
   * @param contextCacheKey - if rendering multiple times in parallel, set this
   * @returns {Promise}
   * @private
   */
  async _handleRenderText(text, contextCacheKey = null) {
    if (!contextCacheKey || !this._getRenderContextPromiseCache[contextCacheKey]) {
      // NOTE: We're caching promises here to avoid race conditions
      this._getRenderContextPromiseCache[contextCacheKey] = this._fetchRenderContext();
    }

    // Set timeout to delete the key eventually
    setTimeout(() => delete this._getRenderContextPromiseCache[contextCacheKey], 5000);

    const context = await this._getRenderContextPromiseCache[contextCacheKey];
    return render.render(text, context);
  }

  _handleGenerateCodeForActiveRequest() {
    App._handleGenerateCode(this.props.activeRequest);
  }

  static _handleGenerateCode(request) {
    showModal(GenerateCodeModal, request);
  }

  async _handleCopyAsCurl(request) {
    const { activeEnvironment } = this.props;
    const environmentId = activeEnvironment ? activeEnvironment._id : 'n/a';
    const har = await exportHarRequest(request._id, environmentId);
    const snippet = new HTTPSnippet(har);
    const cmd = snippet.convert('shell', 'curl');
    clipboard.writeText(cmd);
  }

  static async _updateRequestGroupMetaByParentId(requestGroupId, patch) {
    const requestGroupMeta = await models.requestGroupMeta.getByParentId(requestGroupId);
    if (requestGroupMeta) {
      await models.requestGroupMeta.update(requestGroupMeta, patch);
    } else {
      const newPatch = Object.assign({ parentId: requestGroupId }, patch);
      await models.requestGroupMeta.create(newPatch);
    }
  }

  async _updateActiveWorkspaceMeta(patch) {
    const { activeWorkspaceMeta } = this.props;
    return models.workspaceMeta.update(activeWorkspaceMeta, patch);
  }

  static async _updateRequestMetaByParentId(requestId, patch) {
    const requestMeta = await models.requestMeta.getByParentId(requestId);
    if (requestMeta) {
      return models.requestMeta.update(requestMeta, patch);
    } else {
      const newPatch = Object.assign({ parentId: requestId }, patch);
      return models.requestMeta.create(newPatch);
    }
  }

  _updateIsVariableUncovered() {
    this.setState({ isVariableUncovered: !this.state.isVariableUncovered });
  }

  _handleSetPaneWidth(paneWidth) {
    this.setState({ paneWidth });
    this._savePaneWidth(paneWidth);
  }

  _handleSetPaneHeight(paneHeight) {
    this.setState({ paneHeight });
    this._savePaneHeight(paneHeight);
  }

  async _handleSetActiveRequest(activeRequestId) {
    await this._updateActiveWorkspaceMeta({ activeRequestId });
    await App._updateRequestMetaByParentId(activeRequestId, { lastActive: Date.now() });
  }

  async _handleSetActiveEnvironment(activeEnvironmentId) {
    await this._updateActiveWorkspaceMeta({ activeEnvironmentId });

    // Give it time to update and re-render
    setTimeout(() => {
      this._wrapper && this._wrapper._forceRequestPaneRefresh();
    }, 300);
  }

  _handleSetSidebarWidth(sidebarWidth) {
    this.setState({ sidebarWidth });
    this._saveSidebarWidth(sidebarWidth);
  }

  async _handleSetSidebarHidden(sidebarHidden) {
    await this._updateActiveWorkspaceMeta({ sidebarHidden });
  }

  async _handleSetSidebarFilter(sidebarFilter) {
    await this._updateActiveWorkspaceMeta({ sidebarFilter });
  }

  _handleSetRequestGroupCollapsed(requestGroupId, collapsed) {
    App._updateRequestGroupMetaByParentId(requestGroupId, { collapsed });
  }

  async _handleSetRequestPinned(request, pinned) {
    App._updateRequestMetaByParentId(request._id, { pinned });
  }

  _handleSetResponsePreviewMode(requestId, previewMode) {
    App._updateRequestMetaByParentId(requestId, { previewMode });
  }

  _handleUpdateDownloadPath(requestId, downloadPath) {
    App._updateRequestMetaByParentId(requestId, { downloadPath });
  }

  async _handleSetResponseFilter(requestId, responseFilter) {
    await App._updateRequestMetaByParentId(requestId, { responseFilter });

    clearTimeout(this._responseFilterHistorySaveTimeout);
    this._responseFilterHistorySaveTimeout = setTimeout(async () => {
      const meta = await models.requestMeta.getByParentId(requestId);
      const responseFilterHistory = meta.responseFilterHistory.slice(0, 10);

      // Already in history?
      if (responseFilterHistory.includes(responseFilter)) {
        return;
      }

      // Blank?
      if (!responseFilter) {
        return;
      }

      responseFilterHistory.unshift(responseFilter);
      await App._updateRequestMetaByParentId(requestId, {
        responseFilterHistory,
      });
    }, 2000);
  }

  async _handleUpdateRequestMimeType(mimeType) {
    if (!this.props.activeRequest) {
      console.warn('Tried to update request mime-type when no active request');
      return null;
    }

    const requestMeta = await models.requestMeta.getOrCreateByParentId(
      this.props.activeRequest._id,
    );
    const savedBody = requestMeta.savedRequestBody;

    const saveValue =
      typeof mimeType !== 'string' // Switched to No body
        ? this.props.activeRequest.body
        : {}; // Clear saved value in requestMeta

    await models.requestMeta.update(requestMeta, {
      savedRequestBody: saveValue,
    });

    const newRequest = await updateMimeType(this.props.activeRequest, mimeType, false, savedBody);

    // Force it to update, because other editor components (header editor)
    // needs to change. Need to wait a delay so the next render can finish
    setTimeout(() => {
      this.setState({ forceRefreshHeaderCounter: this.state.forceRefreshHeaderCounter + 1 });
    }, 500);

    return newRequest;
  }

  async _getDownloadLocation() {
    return new Promise(resolve => {
      const options = {
        title: 'Select Download Location',
        buttonLabel: 'Send and Save',
        defaultPath: window.localStorage.getItem('insomnia.sendAndDownloadLocation'),
      };

      remote.dialog.showSaveDialog(options, filename => {
        window.localStorage.setItem('insomnia.sendAndDownloadLocation', filename);
        resolve(filename);
      });
    });
  }

  async _handleSendAndDownloadRequestWithEnvironment(requestId, environmentId, dir) {
    const { settings, handleStartLoading, handleStopLoading } = this.props;

    const request = await models.request.getById(requestId);
    if (!request) {
      return;
    }

    // NOTE: Since request is by far the most popular event, we will throttle
    // it so that we only track it if the request has changed since the last one
    const key = request._id;
    if (this._sendRequestTrackingKey !== key) {
      this._sendRequestTrackingKey = key;
    }

    // Start loading
    handleStartLoading(requestId);

    try {
      const responsePatch = await network.send(requestId, environmentId);
      const headers = responsePatch.headers || [];
      const header = getContentDispositionHeader(headers);
      const nameFromHeader = header ? header.value : null;

      if (
        responsePatch.bodyPath &&
        responsePatch.statusCode >= 200 &&
        responsePatch.statusCode < 300
      ) {
        const extension = mime.extension(responsePatch.contentType) || 'unknown';
        const name =
          nameFromHeader || `${request.name.replace(/\s/g, '-').toLowerCase()}.${extension}`;

        let filename;
        if (dir) {
          filename = path.join(dir, name);
        } else {
          filename = await this._getDownloadLocation();
        }

        const to = fs.createWriteStream(filename);
        const readStream = models.response.getBodyStream(responsePatch);

        if (!readStream) {
          return;
        }

        readStream.pipe(to);

        readStream.on('end', async () => {
          responsePatch.error = `Saved to ${filename}`;
          await models.response.create(responsePatch, settings.maxHistoryResponses);
        });

        readStream.on('error', async err => {
          console.warn('Failed to download request after sending', responsePatch.bodyPath, err);
          await models.response.create(responsePatch, settings.maxHistoryResponses);
        });
      } else {
        // Save the bad responses so failures are shown still
        await models.response.create(responsePatch, settings.maxHistoryResponses);
      }
    } catch (err) {
      showAlert({
        title: 'Unexpected Request Failure',
        message: (
          <div>
            <p>The request failed due to an unhandled error:</p>
            <code className="wide selectable">
              <pre>{err.message}</pre>
            </code>
          </div>
        ),
      });
    }

    // Unset active response because we just made a new one
    await App._updateRequestMetaByParentId(requestId, {
      activeResponseId: null,
    });

    // Stop loading
    handleStopLoading(requestId);
  }

  async _handleSendRequestWithEnvironment(requestId, environmentId) {
    const { handleStartLoading, handleStopLoading, settings } = this.props;
    const request = await models.request.getById(requestId);
    if (!request) {
      return;
    }

    // NOTE: Since request is by far the most popular event, we will throttle
    // it so that we only track it if the request has changed since the last noe
    const key = `${request._id}::${request.modified}`;
    if (this._sendRequestTrackingKey !== key) {
      this._sendRequestTrackingKey = key;
    }

    handleStartLoading(requestId);

    try {
      const responsePatch = await network.send(requestId, environmentId);
      await models.response.create(responsePatch, settings.maxHistoryResponses);
    } catch (err) {
      if (err.type === 'render') {
        showModal(RequestRenderErrorModal, { request, error: err });
      } else {
        showAlert({
          title: 'Unexpected Request Failure',
          message: (
            <div>
              <p>The request failed due to an unhandled error:</p>
              <code className="wide selectable">
                <pre>{err.message}</pre>
              </code>
            </div>
          ),
        });
      }
    }

    // Unset active response because we just made a new one
    await App._updateRequestMetaByParentId(requestId, {
      activeResponseId: null,
    });

    // Stop loading
    handleStopLoading(requestId);
  }

  async _handleSetActiveResponse(requestId, activeResponse = null) {
    const { activeEnvironment } = this.props;
    const activeResponseId = activeResponse ? activeResponse._id : null;
    await App._updateRequestMetaByParentId(requestId, { activeResponseId });

    let response;
    if (activeResponseId) {
      response = await models.response.getById(activeResponseId);
    } else {
      const environmentId = activeEnvironment ? activeEnvironment._id : null;
      response = await models.response.getLatestForRequest(requestId, environmentId);
    }

    const requestVersionId = response ? response.requestVersionId : 'n/a';
    const request = await models.requestVersion.restore(requestVersionId);

    if (request) {
      // Refresh app to reflect changes. Using timeout because we need to
      // wait for the request update to propagate.
      setTimeout(() => this._wrapper._forceRequestPaneRefresh(), 500);
    } else {
      // Couldn't restore request. That's okay
    }
  }

  _requestCreateForWorkspace() {
    this._requestCreate(this.props.activeWorkspace._id);
  }

  _startDragSidebar() {
    this.setState({ draggingSidebar: true });
  }

  _resetDragSidebar() {
    // TODO: Remove setTimeout need be not triggering drag on double click
    setTimeout(() => this._handleSetSidebarWidth(DEFAULT_SIDEBAR_WIDTH), 50);
  }

  _startDragPaneHorizontal() {
    this.setState({ draggingPaneHorizontal: true });
  }

  _startDragPaneVertical() {
    this.setState({ draggingPaneVertical: true });
  }

  _resetDragPaneHorizontal() {
    // TODO: Remove setTimeout need be not triggering drag on double click
    setTimeout(() => this._handleSetPaneWidth(DEFAULT_PANE_WIDTH), 50);
  }

  _resetDragPaneVertical() {
    // TODO: Remove setTimeout need be not triggering drag on double click
    setTimeout(() => this._handleSetPaneHeight(DEFAULT_PANE_HEIGHT), 50);
  }

  _handleMouseMove(e) {
    if (this.state.draggingPaneHorizontal) {
      // Only pop the overlay after we've moved it a bit (so we don't block doubleclick);
      const distance = this.props.paneWidth - this.state.paneWidth;
      if (!this.state.showDragOverlay && Math.abs(distance) > 0.02 /* % */) {
        this.setState({ showDragOverlay: true });
      }

      const requestPane = ReactDOM.findDOMNode(this._requestPane);
      const responsePane = ReactDOM.findDOMNode(this._responsePane);

      const requestPaneWidth = requestPane.offsetWidth;
      const responsePaneWidth = responsePane.offsetWidth;

      const pixelOffset = e.clientX - requestPane.offsetLeft;
      let paneWidth = pixelOffset / (requestPaneWidth + responsePaneWidth);
      paneWidth = Math.min(Math.max(paneWidth, MIN_PANE_WIDTH), MAX_PANE_WIDTH);

      this._handleSetPaneWidth(paneWidth);
    } else if (this.state.draggingPaneVertical) {
      // Only pop the overlay after we've moved it a bit (so we don't block doubleclick);
      const distance = this.props.paneHeight - this.state.paneHeight;
      if (!this.state.showDragOverlay && Math.abs(distance) > 0.02 /* % */) {
        this.setState({ showDragOverlay: true });
      }

      const requestPane = ReactDOM.findDOMNode(this._requestPane);
      const responsePane = ReactDOM.findDOMNode(this._responsePane);

      const requestPaneHeight = requestPane.offsetHeight;
      const responsePaneHeight = responsePane.offsetHeight;

      const pixelOffset = e.clientY - requestPane.offsetTop;
      let paneHeight = pixelOffset / (requestPaneHeight + responsePaneHeight);
      paneHeight = Math.min(Math.max(paneHeight, MIN_PANE_HEIGHT), MAX_PANE_HEIGHT);

      this._handleSetPaneHeight(paneHeight);
    } else if (this.state.draggingSidebar) {
      // Only pop the overlay after we've moved it a bit (so we don't block doubleclick);
      const distance = this.props.sidebarWidth - this.state.sidebarWidth;
      if (!this.state.showDragOverlay && Math.abs(distance) > 2 /* ems */) {
        this.setState({ showDragOverlay: true });
      }

      const sidebar = ReactDOM.findDOMNode(this._sidebar);
      const currentPixelWidth = sidebar.offsetWidth;
      const ratio = (e.clientX - sidebar.offsetLeft) / currentPixelWidth;
      const width = this.state.sidebarWidth * ratio;

      let sidebarWidth = Math.min(width, MAX_SIDEBAR_REMS);

      if (sidebarWidth < COLLAPSE_SIDEBAR_REMS) {
        sidebarWidth = MIN_SIDEBAR_REMS;
      }

      this._handleSetSidebarWidth(sidebarWidth);
    }
  }

  _handleMouseUp() {
    if (this.state.draggingSidebar) {
      this.setState({ draggingSidebar: false, showDragOverlay: false });
    }

    if (this.state.draggingPaneHorizontal) {
      this.setState({ draggingPaneHorizontal: false, showDragOverlay: false });
    }

    if (this.state.draggingPaneVertical) {
      this.setState({ draggingPaneVertical: false, showDragOverlay: false });
    }
  }

  _handleKeyDown(e) {
    for (const [definition, callback] of this._globalKeyMap) {
      executeHotKey(e, definition, callback);
    }
  }

  _handleToggleMenuBar(hide) {
    for (const win of remote.BrowserWindow.getAllWindows()) {
      if (win.isMenuBarAutoHide() !== hide) {
        win.setAutoHideMenuBar(hide);
        win.setMenuBarVisibility(!hide);
      }
    }
  }

  async _handleToggleSidebar() {
    const sidebarHidden = !this.props.sidebarHidden;
    await this._handleSetSidebarHidden(sidebarHidden);
  }

  _handleShowExportRequestsModal() {
    showModal(ExportRequestsModal);
  }

  static _handleShowSettingsModal(tabIndex) {
    showModal(SettingsModal, tabIndex);
  }

  _setWrapperRef(n) {
    this._wrapper = n;
  }

  async _handleReloadPlugins() {
    const { settings } = this.props;
    await plugins.reloadPlugins();
    templating.reload();
    themes.setTheme(settings.theme);
    console.log('[plugins] reloaded');
  }

  _handleToggleInsomniaActivity() {
    const { activity, handleSetActiveActivity } = this.props;

    handleSetActiveActivity(activity === ACTIVITY_INSOMNIA ? ACTIVITY_HOME : ACTIVITY_INSOMNIA);
  }

  /**
   * Update document.title to be "Workspace (Environment) – Request" when not home
   * @private
   */
  _updateDocumentTitle() {
    const { activeWorkspace, activeEnvironment, activeRequest } = this.props;

    let title;

    if (this.props.activity === ACTIVITY_HOME) {
      title = getAppName();
    } else {
      title = activeWorkspace.name;
      if (activeEnvironment) {
        title += ` (${activeEnvironment.name})`;
      }
      if (activeRequest) {
        title += ` – ${activeRequest.name}`;
      }
    }

    document.title = title;
  }

  componentDidUpdate(prevProps) {
    this._updateDocumentTitle();
    this._ensureWorkspaceChildren();

    // Force app refresh if login state changes
    if (prevProps.isLoggedIn !== this.props.isLoggedIn) {
      this.setState(state => ({
        forceRefreshCounter: state.forceRefreshCounter + 1,
      }));
    }

    // Check on VCS things
    const { activeWorkspace, activeGitRepository } = this.props;
    const changingWorkspace = prevProps.activeWorkspace._id !== activeWorkspace._id;

    // Update VCS if needed
    if (changingWorkspace) {
      this._updateVCS();
    }

    // Update Git VCS if needed
    const thisGit = activeGitRepository || {};
    const nextGit = prevProps.activeGitRepository || {};
    if (changingWorkspace || thisGit._id !== nextGit._id) {
      this._updateGitVCS();
    }
  }

  async _updateGitVCS() {
    const { activeGitRepository, activeWorkspace } = this.props;

    // Get the vcs and set it to null in the state while we update it
    let gitVCS = this.state.gitVCS;
    this.setState({ gitVCS: null });

    if (!gitVCS) {
      gitVCS = new GitVCS();
    }

    if (activeGitRepository) {
      // Create FS plugin
      const baseDir = path.join(
        getDataDirectory(),
        `version-control/git/${activeGitRepository._id}`,
      );
      const pNeDb = NeDBPlugin.createPlugin(activeWorkspace._id);
      const pGitData = FSPlugin.createPlugin(baseDir);
      const pOtherData = FSPlugin.createPlugin(path.join(baseDir, 'other'));
      const gitSubDir = '/git';

      const fsPlugin = routableFSPlugin(
        // All data outside the directories listed below will be stored in an 'other'
        // directory. This is so we can support files that exist outside the ones
        // the app is specifically in charge of.
        pOtherData,
        {
          // All app data is stored within the a namespaced directory at the root of the
          // repository and is read/written from the local NeDB database
          [`/${GIT_NAMESPACE_DIR}`]: pNeDb,

          // All git metadata is stored in a git/ directory on the filesystem
          [gitSubDir]: pGitData,
        },
      );

      // Init VCS
      if (activeGitRepository.needsFullClone) {
        await models.gitRepository.update(activeGitRepository, { needsFullClone: false });
        const { credentials, uri } = activeGitRepository;
        await gitVCS.initFromClone(uri, credentials, '/', fsPlugin, gitSubDir);
      } else {
        await gitVCS.init('/', fsPlugin, gitSubDir);
      }

      // Configure basic info
      const { author, uri: gitUri } = activeGitRepository;
      await gitVCS.setAuthor(author.name, author.email);
      await gitVCS.addRemote(gitUri);
    } else {
      // Create new one to un-initialize it
      gitVCS = new GitVCS();
    }

    this.setState({ gitVCS });
  }

  async _updateVCS() {
    const { activeWorkspace } = this.props;

    // Get the vcs and set it to null in the state while we update it
    let vcs = this.state.vcs;
    this.setState({ vcs: null });

    if (!vcs) {
      const directory = path.join(getDataDirectory(), 'version-control');
      const driver = new FileSystemDriver({ directory });
      vcs = new VCS(driver, async conflicts => {
        return new Promise(resolve => {
          showModal(SyncMergeModal, {
            conflicts,
            handleDone: conflicts => resolve(conflicts),
          });
        });
      });
    }

    await vcs.switchProject(activeWorkspace._id);

    this.setState({ vcs });
  }

  async componentDidMount() {
    // Bind mouse and key handlers
    document.addEventListener('mouseup', this._handleMouseUp);
    document.addEventListener('mousemove', this._handleMouseMove);
    this._setGlobalKeyMap();

    // Update title
    this._updateDocumentTitle();

    // Update VCS
    await this._updateVCS(this.props.activeWorkspace);
    await this._updateGitVCS(this.props.activeWorkspace);

    db.onChange(async changes => {
      let needsRefresh = false;

      for (const change of changes) {
        const [type, doc, fromSync] = change;

        const { vcs } = this.state;
        const { activeRequest } = this.props;

        // Force refresh if environment changes
        // TODO: Only do this for environments in this workspace (not easy because they're nested)
        if (doc.type === models.environment.type) {
          console.log('[App] Forcing update from environment change', change);
          needsRefresh = true;
        }

        // Force refresh if sync changes the active request
        if (fromSync && activeRequest && doc._id === activeRequest._id) {
          needsRefresh = true;
          console.log('[App] Forcing update from request change', change);
        }

        // Delete VCS project if workspace deleted
        if (vcs && doc.type === models.workspace.type && type === db.CHANGE_REMOVE) {
          await vcs.removeProjectsForRoot(doc._id);
        }
      }

      if (needsRefresh) {
        setTimeout(() => {
          this._wrapper && this._wrapper._forceRequestPaneRefresh();
        }, 300);
      }
    });

    ipcRenderer.on('toggle-preferences', () => {
      App._handleShowSettingsModal();
    });

    ipcRenderer.on('reload-plugins', this._handleReloadPlugins);

    ipcRenderer.on('toggle-insomnia', this._handleToggleInsomniaActivity);

    ipcRenderer.on('toggle-preferences-shortcuts', () => {
      App._handleShowSettingsModal(TAB_INDEX_SHORTCUTS);
    });

    ipcRenderer.on('run-command', (e, commandUri) => {
      const parsed = urlParse(commandUri, true);

      const command = `${parsed.hostname}${parsed.pathname}`;
      const args = JSON.parse(JSON.stringify(parsed.query));
      args.workspaceId = args.workspaceId || this.props.activeWorkspace._id;

      this.props.handleCommand(command, args);
    });

    // NOTE: This is required for "drop" event to trigger.
    document.addEventListener(
      'dragover',
      e => {
        e.preventDefault();
      },
      false,
    );

    document.addEventListener(
      'drop',
      async e => {
        e.preventDefault();
        const { activeWorkspace, handleImportUriToWorkspace } = this.props;
        if (!activeWorkspace) {
          return;
        }

        if (e.dataTransfer.files.length === 0) {
          console.log('[drag] Ignored drop event because no files present');
          return;
        }

        const file = e.dataTransfer.files[0];
        const { path } = file;
        const uri = `file://${path}`;

        await showAlert({
          title: 'Confirm Data Import',
          message: (
            <span>
              Import <code>{path}</code>?
            </span>
          ),
          addCancel: true,
        });

        handleImportUriToWorkspace(activeWorkspace._id, uri);
      },
      false,
    );

    ipcRenderer.on('toggle-sidebar', this._handleToggleSidebar);

    // handle this
    this._handleToggleMenuBar(this.props.settings.autoHideMenuBar);

    // Give it a bit before letting the backend know it's ready
    setTimeout(() => ipcRenderer.send('window-ready'), 500);
  }

  componentWillUnmount() {
    // Remove mouse and key handlers
    document.removeEventListener('mouseup', this._handleMouseUp);
    document.removeEventListener('mousemove', this._handleMouseMove);
  }

  async _ensureWorkspaceChildren() {
    const {
      activeWorkspace,
      activeWorkspaceMeta,
      activeCookieJar,
      environments,
      activeApiSpec,
    } = this.props;
    const baseEnvironments = environments.filter(e => e.parentId === activeWorkspace._id);

    // Nothing to do
    if (baseEnvironments.length && activeCookieJar && activeApiSpec && activeWorkspaceMeta) {
      return;
    }

    // We already started migrating. Let it finish.
    if (this.state.isMigratingChildren) {
      return;
    }

    // Prevent rendering of everything
    this.setState({ isMigratingChildren: true }, async () => {
      const flushId = await db.bufferChanges();
      await models.environment.getOrCreateForWorkspace(activeWorkspace);
      await models.cookieJar.getOrCreateForParentId(activeWorkspace._id);
      await models.apiSpec.getOrCreateForParentId(activeWorkspace._id);
      await models.workspaceMeta.getOrCreateByParentId(activeWorkspace._id);
      await db.flushChanges(flushId);

      this.setState({ isMigratingChildren: false });
    });
  }

  // eslint-disable-next-line camelcase
  UNSAFE_componentWillReceiveProps(nextProps) {
    this._ensureWorkspaceChildren(nextProps);

    // Update VCS if needed
    const { activeWorkspace } = this.props;
    if (nextProps.activeWorkspace._id !== activeWorkspace._id) {
      this._updateVCS(nextProps.activeWorkspace);
    }
  }

  // eslint-disable-next-line camelcase
  UNSAFE_componentWillMount() {
    this._ensureWorkspaceChildren(this.props);
  }

  render() {
    if (this.state.isMigratingChildren) {
      console.log('[app] Waiting for migration to complete');
      return null;
    }

    const { activeWorkspace } = this.props;

    const {
      paneWidth,
      paneHeight,
      sidebarWidth,
      isVariableUncovered,
      gitVCS,
      vcs,
      forceRefreshCounter,
      forceRefreshHeaderCounter,
    } = this.state;

    const uniquenessKey = `${forceRefreshCounter}::${activeWorkspace._id}`;

    return (
      <KeydownBinder onKeydown={this._handleKeyDown}>
        <div className="app" key={uniquenessKey}>
          <ErrorBoundary showAlert>
            <Wrapper
              {...this.props}
              ref={this._setWrapperRef}
              paneWidth={paneWidth}
              paneHeight={paneHeight}
              sidebarWidth={sidebarWidth}
              handleCreateRequestForWorkspace={this._requestCreateForWorkspace}
              handleSetRequestPinned={this._handleSetRequestPinned}
              handleSetRequestGroupCollapsed={this._handleSetRequestGroupCollapsed}
              handleActivateRequest={this._handleSetActiveRequest}
              handleSetRequestPaneRef={this._setRequestPaneRef}
              handleSetResponsePaneRef={this._setResponsePaneRef}
              handleSetSidebarRef={this._setSidebarRef}
              handleStartDragSidebar={this._startDragSidebar}
              handleResetDragSidebar={this._resetDragSidebar}
              handleStartDragPaneHorizontal={this._startDragPaneHorizontal}
              handleStartDragPaneVertical={this._startDragPaneVertical}
              handleResetDragPaneHorizontal={this._resetDragPaneHorizontal}
              handleResetDragPaneVertical={this._resetDragPaneVertical}
              handleCreateRequest={this._requestCreate}
              handleRender={this._handleRenderText}
              handleGetRenderContext={this._handleGetRenderContext}
              handleDuplicateRequest={this._requestDuplicate}
              handleDuplicateRequestGroup={App._requestGroupDuplicate}
              handleMoveRequestGroup={App._requestGroupMove}
              handleDuplicateWorkspace={this._workspaceDuplicate}
              handleDuplicateWorkspaceById={this._workspaceDuplicateById}
              handleRenameWorkspace={this._workspaceRename}
              handleDeleteWorkspaceById={this._workspaceDeleteById}
              handleCreateRequestGroup={this._requestGroupCreate}
              handleGenerateCode={App._handleGenerateCode}
              handleGenerateCodeForActiveRequest={this._handleGenerateCodeForActiveRequest}
              handleCopyAsCurl={this._handleCopyAsCurl}
              handleSetResponsePreviewMode={this._handleSetResponsePreviewMode}
              handleSetResponseFilter={this._handleSetResponseFilter}
              handleSendRequestWithEnvironment={this._handleSendRequestWithEnvironment}
              handleSendAndDownloadRequestWithEnvironment={
                this._handleSendAndDownloadRequestWithEnvironment
              }
              handleSetActiveResponse={this._handleSetActiveResponse}
              handleSetActiveRequest={this._handleSetActiveRequest}
              handleSetActiveEnvironment={this._handleSetActiveEnvironment}
              handleSetSidebarFilter={this._handleSetSidebarFilter}
              handleToggleMenuBar={this._handleToggleMenuBar}
              handleUpdateRequestMimeType={this._handleUpdateRequestMimeType}
              handleShowExportRequestsModal={this._handleShowExportRequestsModal}
              handleShowSettingsModal={App._handleShowSettingsModal}
              handleUpdateDownloadPath={this._handleUpdateDownloadPath}
              isVariableUncovered={isVariableUncovered}
              headerEditorKey={forceRefreshHeaderCounter + ''}
              vcs={vcs}
              gitVCS={gitVCS}
            />
          </ErrorBoundary>

          <ErrorBoundary showAlert>
            <Toast />
          </ErrorBoundary>

          {/* Block all mouse activity by showing an overlay while dragging */}
          {this.state.showDragOverlay ? <div className="blocker-overlay" /> : null}
        </div>
      </KeydownBinder>
    );
  }
}

App.propTypes = {
  // Required
  sidebarWidth: PropTypes.number.isRequired,
  paneWidth: PropTypes.number.isRequired,
  paneHeight: PropTypes.number.isRequired,
  handleCommand: PropTypes.func.isRequired,
  settings: PropTypes.object.isRequired,
  isLoggedIn: PropTypes.bool.isRequired,
  activeWorkspace: PropTypes.shape({
    _id: PropTypes.string.isRequired,
  }).isRequired,
  handleSetActiveActivity: PropTypes.func.isRequired,
  handleSetActiveWorkspace: PropTypes.func.isRequired,

  // Optional
  activeRequest: PropTypes.object,
  activeEnvironment: PropTypes.shape({
    _id: PropTypes.string.isRequired,
  }),
};

function mapStateToProps(state, props) {
  const { entities, global } = state;

  const { activeActivity, isLoading, loadingRequestIds, isLoggedIn } = global;

  // Entities
  const entitiesLists = selectEntitiesLists(state, props);
  const {
    workspaces,
    workspaceMetas,
    environments,
    requests,
    requestGroups,
    requestMetas,
    requestVersions,
    apiSpecs,
    gitRepositories,
  } = entitiesLists;

  const settings = entitiesLists.settings[0];

  // Workspace stuff
  const activeWorkspaceMeta = selectActiveWorkspaceMeta(state, props);
  const activeWorkspace = selectActiveWorkspace(state, props);
  const activeWorkspaceClientCertificates = selectActiveWorkspaceClientCertificates(state, props);
  const activeGitRepository = selectActiveGitRepository(state, props);

  const safeMeta = activeWorkspaceMeta || {};
  const sidebarHidden = safeMeta.sidebarHidden || false;
  const sidebarFilter = safeMeta.sidebarFilter || '';
  const sidebarWidth = safeMeta.sidebarWidth || DEFAULT_SIDEBAR_WIDTH;
  const paneWidth = safeMeta.paneWidth || DEFAULT_PANE_WIDTH;
  const paneHeight = safeMeta.paneHeight || DEFAULT_PANE_HEIGHT;

  // Request stuff
  const requestMeta = selectActiveRequestMeta(state, props) || {};
  const activeRequest = selectActiveRequest(state, props);
  const responsePreviewMode = requestMeta.previewMode || PREVIEW_MODE_SOURCE;
  const responseFilter = requestMeta.responseFilter || '';
  const responseFilterHistory = requestMeta.responseFilterHistory || [];
  const responseDownloadPath = requestMeta.downloadPath || null;

  // Cookie Jar
  const activeCookieJar = selectActiveCookieJar(state, props);

  // Response stuff
  const activeRequestResponses = selectActiveRequestResponses(state, props) || [];
  const activeResponse = selectActiveResponse(state, props) || null;

  // Environment stuff
  const activeEnvironmentId = safeMeta.activeEnvironmentId;
  const activeEnvironment = entities.environments[activeEnvironmentId];

  // OAuth2Token stuff
  const oAuth2Token = selectActiveOAuth2Token(state, props);

  // Find other meta things
  const loadStartTime = loadingRequestIds[activeRequest ? activeRequest._id : 'n/a'] || -1;
  const sidebarChildren = selectSidebarChildren(state, props);
  const workspaceChildren = selectWorkspaceRequestsAndRequestGroups(state, props);
  const unseenWorkspaces = selectUnseenWorkspaces(state, props);

  // Sync stuff
  const syncItems = selectSyncItems(state, props);

  // Api spec stuff
  const activeApiSpec = apiSpecs.find(s => s.parentId === activeWorkspace._id);

  return Object.assign({}, state, {
    activity: activeActivity,
    activeApiSpec,
    activeCookieJar,
    activeEnvironment,
    activeGitRepository,
    activeRequest,
    activeRequestResponses,
    activeResponse,
    activeWorkspace,
    activeWorkspaceClientCertificates,
    activeWorkspaceMeta,
    apiSpecs,
    environments,
    gitRepositories,
    isLoading,
    isLoggedIn,
    loadStartTime,
    oAuth2Token,
    paneHeight,
    paneWidth,
    requestGroups,
    requestMetas,
    requestVersions,
    requests,
    responseDownloadPath,
    responseFilter,
    responseFilterHistory,
    responsePreviewMode,
    settings,
    sidebarChildren,
    sidebarFilter,
    sidebarHidden,
    sidebarWidth,
    syncItems,
    unseenWorkspaces,
    workspaceChildren,
    workspaces,
    workspaceMetas,
  });
}

function mapDispatchToProps(dispatch) {
  const global = bindActionCreators(globalActions, dispatch);
  const entities = bindActionCreators(entitiesActions, dispatch);

  return {
    handleStartLoading: global.loadRequestStart,
    handleStopLoading: global.loadRequestStop,

    handleSetActiveActivity: global.setActiveActivity,
    handleSetActiveWorkspace: global.setActiveWorkspace,
    handleImportFileToWorkspace: global.importFile,
    handleImportClipBoardToWorkspace: global.importClipBoard,
    handleImportUriToWorkspace: global.importUri,
    handleCommand: global.newCommand,
    handleExportFile: global.exportWorkspacesToFile,
    handleExportRequestsToFile: global.exportRequestsToFile,
    handleInitializeEntities: entities.initialize,
    handleMoveDoc: _moveDoc,
  };
}

async function _moveDoc(docToMove, parentId, targetId, targetOffset) {
  // Nothing to do. We are in the same spot as we started
  if (docToMove._id === targetId) {
    return;
  }

  // Don't allow dragging things into itself or children. This will disconnect
  // the node from the tree and cause the item to no longer show in the UI.
  const descendents = await db.withDescendants(docToMove);
  for (const doc of descendents) {
    if (doc._id === parentId) {
      return;
    }
  }

  function __updateDoc(doc, patch) {
    models.getModel(docToMove.type).update(doc, patch);
  }

  if (targetId === null) {
    // We are moving to an empty area. No sorting required
    await __updateDoc(docToMove, { parentId });
    return;
  }

  // NOTE: using requestToTarget's parentId so we can switch parents!
  const docs = [
    ...(await models.request.findByParentId(parentId)),
    ...(await models.requestGroup.findByParentId(parentId)),
  ].sort((a, b) => (a.metaSortKey < b.metaSortKey ? -1 : 1));

  // Find the index of doc B so we can re-order and save everything
  for (let i = 0; i < docs.length; i++) {
    const doc = docs[i];

    if (doc._id === targetId) {
      let before, after;
      if (targetOffset < 0) {
        // We're moving to below
        before = docs[i];
        after = docs[i + 1];
      } else {
        // We're moving to above
        before = docs[i - 1];
        after = docs[i];
      }

      const beforeKey = before ? before.metaSortKey : docs[0].metaSortKey - 100;
      const afterKey = after ? after.metaSortKey : docs[docs.length - 1].metaSortKey + 100;

      if (Math.abs(afterKey - beforeKey) < 0.000001) {
        // If sort keys get too close together, we need to redistribute the list. This is
        // not performant at all (need to update all siblings in DB), but it is extremely rare
        // anyway
        console.log(`[app] Recreating Sort Keys ${beforeKey} ${afterKey}`);

        await db.bufferChanges(300);
        docs.map((r, i) => __updateDoc(r, { metaSortKey: i * 100, parentId }));
      } else {
        const metaSortKey = afterKey - (afterKey - beforeKey) / 2;
        __updateDoc(docToMove, { metaSortKey, parentId });
      }

      break;
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(App);


"name": "now",
"core": true,
"unlisted": true

insomnia-plugin-core-themes

Insomnia core themes


import marked from 'marked';

marked.setOptions({
  renderer: new marked.Renderer(),
  gfm: true,
  tables: true,
  breaks: false,
  pedantic: false,
  sanitize: true,
  smartLists: true,
  smartypants: false,
});

export function markdownToHTML(markdown) {
  return marked(markdown);
}


// @flow
import * as React from 'react';
import Hotkey from '../../hotkey';
import type { KeyBindings } from '../../../../common/hotkeys';

type Props = {
  keyBindings: KeyBindings,
};

class DropdownHint extends React.PureComponent<Props> {
  render() {
    const { keyBindings } = this.props;
    return <Hotkey className="dropdown__hint" keyBindings={keyBindings} />;
  }
}

export default DropdownHint;


// @flow
import * as _app from './app';
import * as _data from './data';
import * as _network from './network';
import * as _request from './request';
import * as _response from './response';
import * as _store from './store';

export const app = _app;
export const data = _data;
export const network = _network;
export const request = _request;
export const response = _response;
export const store = _store;


keep keep keep replace keep keep keep keep keep

"name": "prompt",
"core": true,
"unlisted": true

insomnia-plugin-now

Insomnia now template tag


# Kong Portal Deploy (Insomnia Plugin)

[![Npm Version](https://img.shields.io/npm/v/insomnia-plugin-kong-portal.svg)](https://www.npmjs.com/package/insomnia-plugin-kong-portal)

This is a plugin for [Insomnia Designer](https://insomnia.rest) to add the ability to deploy
API specifications to Kong Portal.

## Installation

Install the `insomnia-plugin-kong-portal` plugin from Preferences > Plugins.


// @flow
import YAML from 'yaml';
import * as React from 'react';
import autobind from 'autobind-decorator';
import path from 'path';
import * as models from '../../../models';
import Modal from '../base/modal';
import ModalBody from '../base/modal-body';
import ModalHeader from '../base/modal-header';
import type { Workspace } from '../../../models/workspace';
import GitVCS, { GIT_NAMESPACE_DIR } from '../../../sync/git/git-vcs';
import { withDescendants } from '../../../common/database';
import IndeterminateCheckbox from '../base/indeterminate-checkbox';
import ModalFooter from '../base/modal-footer';
import Tooltip from '../tooltip';

type Props = {|
  workspace: Workspace,
  vcs: GitVCS,
|};

type Item = {|
  path: string,
  type: string,
  status: string,
  staged: boolean,
  editable: boolean,
|};

type State = {|
  branch: string,
  message: string,
  items: {
    [string]: Item,
  },
|};

const INITIAL_STATE = {
  branch: '',
  message: '',
  items: {},
};

@autobind
class GitStagingModal extends React.PureComponent<Props, State> {
  modal: ?Modal;
  statusNames: { [string]: string };
  textarea: ?HTMLTextAreaElement;
  onCommit: null | (() => void);

  constructor(props: Props) {
    super(props);
    this.state = INITIAL_STATE;
    this.onCommit = null;
  }

  _setModalRef(ref: ?Modal) {
    this.modal = ref;
  }

  _setTextareaRef(ref: ?Modal) {
    this.textarea = ref;
  }

  async _handleMessageChange(e: SyntheticEvent<HTMLTextAreaElement>) {
    this.setState({ message: e.currentTarget.value });
  }

  async _handleCommit(e: SyntheticEvent<HTMLButtonElement>) {
    const { vcs } = this.props;
    const { items, message } = this.state;

    // Set the stage
    for (const p of Object.keys(items)) {
      const item = items[p];

      if (!item.staged) {
        continue;
      }

      if (item.status.includes('deleted')) {
        await vcs.remove(item.path);
      } else {
        await vcs.add(item.path);
      }
    }

    await vcs.commit(message);

    this.modal && this.modal.hide();

    if (typeof this.onCommit === 'function') {
      this.onCommit();
    }
  }

  async _toggleAll(items: Array<Item>, forceAdd?: boolean = false) {
    const allStaged = items.every(i => i.staged);
    const doStage = !allStaged;

    const newItems = { ...this.state.items };

    for (const { path: p } of items) {
      if (!newItems[p].editable) {
        continue;
      }

      newItems[p].staged = doStage || forceAdd;
    }

    this.setState({ items: newItems });
  }

  async _handleToggleOne(e: SyntheticEvent<HTMLInputElement>) {
    const newItems = { ...this.state.items };

    const gitPath = e.currentTarget.name;

    if (!newItems[gitPath] || !newItems[gitPath].editable) {
      return;
    }

    newItems[gitPath].staged = !newItems[gitPath].staged;

    this.setState({ items: newItems });
  }

  async getAllPaths(): Promise<Array<string>> {
    const { vcs } = this.props;

    const f = vcs.getFs().promises;
    const rootDir = path.join('/', GIT_NAMESPACE_DIR);

    const fsPaths = [];
    for (const type of await f.readdir(rootDir)) {
      const typeDir = path.join(rootDir, type);
      for (const name of await f.readdir(typeDir)) {
        // NOTE: git paths don't start with '/' so we're omitting
        //  it here too.
        const gitPath = path.join(`${GIT_NAMESPACE_DIR}/`, type, name);
        fsPaths.push(path.join(gitPath));
      }
    }

    // To get all possible paths, we need to combine the paths already in Git
    // with the paths on the FS. This is required to cover the case where a
    // file can be deleted from FS or from Git.
    const gitPaths = await vcs.listFiles();
    const uniquePaths = new Set([...fsPaths, ...gitPaths]);

    return Array.from(uniquePaths).sort();
  }

  async show(options: { onCommit?: () => void }) {
    const { vcs, workspace } = this.props;

    this.onCommit = options.onCommit || null;

    this.modal && this.modal.show();

    // Reset state
    this.setState(INITIAL_STATE);

    // Cache status names
    const docs = await withDescendants(workspace);
    const allPaths = await this.getAllPaths();

    this.statusNames = {};
    for (const doc of docs) {
      this.statusNames[path.join(GIT_NAMESPACE_DIR, doc.type, `${doc._id}.json`)] = (doc: any).name || '';
      this.statusNames[path.join(GIT_NAMESPACE_DIR, doc.type, `${doc._id}.yml`)] = (doc: any).name || '';
    }

    // Create status items
    const items = {};
    const log = (await vcs.log()) || [];
    for (const gitPath of allPaths) {
      const status = await vcs.status(gitPath);
      if (status === 'unmodified') {
        continue;
      }

      if (!this.statusNames[gitPath] && log.length > 0) {
        const docYML = await vcs.readObjFromTree(log[0].tree, gitPath);
        if (!docYML) {
          continue;
        }

        try {
          const doc = YAML.parse(docYML);
          this.statusNames[gitPath] = (doc: any).name || '';
        } catch (err) {
          // Nothing here
        }
      }

      // We know that type is in the path but we don't know where. This
      // is the safest way to check for the type
      let type = 'Unknown';
      for (const t of models.types()) {
        if (gitPath.includes(t)) {
          type = t;
          break;
        }
      }

      let staged = !status.includes('added');

      // We want to enforce that the workspace is committed because otherwise
      // others won't be able to clone from it. So here we're preventing
      // people from un-staging the workspace if it's not added yet.
      let editable = true;
      if (type === models.workspace.type && status.includes('added')) {
        editable = false;
        staged = true;
      }

      items[gitPath] = {
        type,
        staged,
        editable,
        status,
        path: gitPath,
      };
    }

    const branch = await vcs.getBranch();
    this.setState(
      {
        items,
        branch,
      },
      () => {
        this.textarea && this.textarea.focus();
      },
    );
  }

  renderOperation(item: Item) {
    let child = null;
    let message = '';

    if (item.status.includes('added')) {
      child = ;
      message = 'Added';
    } else if (item.status.includes('modified')) {
      child = ;
      message = 'Modified';
    } else if (item.status.includes('deleted')) {
      child = ;
      message = 'Deleted';
    } else {
      child = ;
      message = 'Unknown';
    }

    return (
      <React.Fragment>
        <Tooltip message={message}>
          {child} {item.type}
        </Tooltip>
      </React.Fragment>
    );
  }

  renderItem(item: Item) {
    const { path: gitPath, staged, editable } = item;

    const docName = this.statusNames[gitPath] || 'n/a';

    return (

            <input
              disabled={!editable}
              className="space-right"
              type="checkbox"
              checked={staged}
              name={gitPath}
              onChange={this._handleToggleOne}
            />{' '}
            {docName}

        {this.renderOperation(item)}

    );
  }

  renderTable(title: string, items: Array<Item>) {
    if (items.length === 0) {
      return null;
    }

    const allStaged = items.every(i => i.staged);
    const allUnstaged = items.every(i => !i.staged);

    return (

        {title}

                    <IndeterminateCheckbox
                      className="space-right"
                      type="checkbox"
                      checked={allStaged}
                      onChange={() => this._toggleAll(items, !allStaged)}
                      indeterminate={!allStaged && !allUnstaged}
                    />
                  {' '}
                  name

              Description

{items.map(this.renderItem)}

    );
  }

  render() {
    const { items, message, branch } = this.state;

    const itemsList = Object.keys(items).map(k => items[k]);
    const addedItems = itemsList.filter(i => i.status.includes('added'));
    const nonAddedItems = itemsList.filter(i => !i.status.includes('added'));

    return (
      <Modal ref={this._setModalRef}>
        <ModalHeader>Commit Changes</ModalHeader>
        <ModalBody className="wide pad">

            <textarea
              ref={this._setTextareaRef}
              rows="3"
              required
              placeholder="A descriptive message to describe changes made"
              defaultValue={message}
              onChange={this._handleMessageChange}
            />

          {this.renderTable('Modified Objects', nonAddedItems)}
          {this.renderTable('Unversioned Objects', addedItems)}
        </ModalBody>
        <ModalFooter>

             {branch}

            <button className="btn" onClick={this._handleCommit}>
              Commit
            </button>

        </ModalFooter>
      </Modal>
    );
  }
}

export default GitStagingModal;


Complex URL Test Collection

A collection for testing


keep keep keep keep replace keep keep keep keep

"name": "request",
"core": true,
"unlisted": true

insomnia-plugin-prompt

Insomnia prompt template tag


a {
  font-weight: 600 !important;
  text-decoration: none;

  &:not(.a--nocolor) {
    color: var(--color-surprise);
  }

  &:hover {
    text-decoration: underline;
  }
}


// @flow

declare module 'reselect' {
  declare module.exports: {
    createSelector: Function,
  };
}


FROM snapcore/snapcraft:stable

LABEL "name"="Insomnia-Ubuntu-16"
LABEL "maintainer"="Gregory Schier <greg.schier@konghq.com>"
LABEL "version"="1.0.0"

LABEL "com.github.actions.icon"="package"
LABEL "com.github.actions.color"="blue"
LABEL "com.github.actions.name"="Insomnia Ubuntu 16"
LABEL "com.github.actions.description"="Build and package on Ubuntu 16"

COPY entrypoint.sh /scripts/entrypoint.sh
COPY install-dependencies.sh /scripts/install-dependencies.sh

RUN chmod +x /scripts/*
RUN /scripts/install-dependencies.sh

ENTRYPOINT ["/scripts/entrypoint.sh"]


"name": "response",
"core": true,
"unlisted": true

insomnia-plugin-file

Insomnia file templte tag


import { parse as urlParse } from 'url';
import { setDefaultProtocol } from 'insomnia-url';
import axios from 'axios';
import * as models from '../models';
import { isDevelopment } from '../common/constants';

export async function axiosRequest(config) {
  const settings = await models.settings.getOrCreate();
  const isHttps = config.url.indexOf('https:') === 0;

  let proxyUrl = null;
  if (isHttps && settings.httpsProxy) {
    proxyUrl = settings.httpsProxy;
  } else if (settings.httpProxy) {
    proxyUrl = settings.httpProxy;
  }

  const finalConfig = {
    ...config,
    adapter: global.require('axios/lib/adapters/http'),
  };

  if (proxyUrl) {
    const { hostname, port } = urlParse(setDefaultProtocol(proxyUrl));
    finalConfig.proxy = { host: hostname, port };
  }

  const response = await axios(finalConfig);

  if (isDevelopment()) {
    console.log('[axios] Response', { config, response });
  }

  return response;
}


"name": "uuid",
"core": true,
"unlisted": true

Insomnia file templte tag


// @flow

declare module 'pdfjs-dist/webpack' {
  declare module.exports: *;
}


// @flow

declare module 'fuzzysort' {
  declare module.exports: {
    highlight: Function,
    single: Function,
  };
}


// @flow
import * as React from 'react';
import { Tab, TabList, TabPanel, Tabs } from 'react-tabs';
import autobind from 'autobind-decorator';
import * as toughCookie from 'tough-cookie';
import * as models from '../../../models';
import clone from 'clone';
import { DEBOUNCE_MILLIS } from '../../../common/constants';
import Modal from '../base/modal';
import ModalBody from '../base/modal-body';
import ModalHeader from '../base/modal-header';
import ModalFooter from '../base/modal-footer';
import OneLineEditor from '../codemirror/one-line-editor';
import { cookieToString } from 'insomnia-cookies';
import type { Cookie, CookieJar } from '../../../models/cookie-jar';
import type { Workspace } from '../../../models/workspace';

type Props = {
  handleRender: Function,
  handleGetRenderContext: Function,
  nunjucksPowerUserMode: boolean,
  isVariableUncovered: boolean,
  workspace: Workspace,
  cookieJar: CookieJar,
};

type State = {
  cookie: Cookie | null,
  rawValue: string,
};

@autobind
class CookieModifyModal extends React.PureComponent<Props, State> {
  modal: Modal | null;
  _rawTimeout: TimeoutID;
  _cookieUpdateTimeout: TimeoutID;

  constructor(props: any) {
    super(props);

    this.state = {
      cookie: null,
      rawValue: '',
    };
  }

  _setModalRef(n: Modal | null) {
    this.modal = n;
  }

  async show(cookie: Cookie) {
    // Dunno why this is sent as an array
    cookie = cookie[0] || cookie;

    const { cookieJar } = this.props;
    const oldCookie = cookieJar.cookies.find(c => c.id === cookie.id);

    if (!oldCookie) {
      // Cookie not found in jar
      return;
    }

    this.setState({ cookie });

    this.modal && this.modal.show();
  }

  hide() {
    this.modal && this.modal.hide();
  }

  static async _saveChanges(cookieJar: CookieJar) {
    await models.cookieJar.update(cookieJar);
  }

  _handleChangeRawValue(e: SyntheticEvent<HTMLInputElement>) {
    const value = e.currentTarget.value;

    clearTimeout(this._rawTimeout);
    this._rawTimeout = setTimeout(async () => {
      const oldCookie = this.state.cookie;
      let cookie;
      try {
        // NOTE: Perform toJSON so we have a plain JS object instead of Cookie instance
        cookie = toughCookie.Cookie.parse(value).toJSON();
      } catch (err) {
        console.warn(`Failed to parse cookie string "${value}"`, err);
        return;
      }

      if (!this.state.cookie || !oldCookie) {
        return;
      }

      // Make sure cookie has an id
      cookie.id = oldCookie.id;

      await this._handleCookieUpdate(cookie);
    }, DEBOUNCE_MILLIS * 2);
  }

  async _handleCookieUpdate(newCookie: Cookie) {
    const oldCookie = this.state.cookie;
    if (!oldCookie) {
      // We don't have a cookie to edit
      return;
    }

    const cookie = clone(newCookie);

    // Sanitize expires field
    const expires = new Date(cookie.expires || '').getTime();
    if (isNaN(expires)) {
      cookie.expires = null;
    } else {
      cookie.expires = expires;
    }

    // Clone so we don't modify the original
    const cookieJar = clone(this.props.cookieJar);

    const { cookies } = cookieJar;
    const index = cookies.findIndex(c => c.id === cookie.id);

    if (index < 0) {
      console.warn(`Could not find cookie with id=${cookie.id} to edit`);
      return;
    }

    cookieJar.cookies = [...cookies.slice(0, index), cookie, ...cookies.slice(index + 1)];

    this.setState({ cookie });

    await CookieModifyModal._saveChanges(cookieJar);

    return cookie;
  }

  _handleChange(field: string, eventOrValue: string | Event) {
    const { cookie } = this.state;

    let value;

    if (typeof eventOrValue === 'string') {
      value = eventOrValue.trim();
    } else if (eventOrValue.target instanceof HTMLInputElement) {
      if (eventOrValue.target.type === 'checkbox') {
        value = eventOrValue.target.checked;
      } else {
        value = eventOrValue.target.value.trim();
      }
    } else {
      // Should never happen
    }

    const newCookie = Object.assign({}, cookie, { [field]: value });

    clearTimeout(this._cookieUpdateTimeout);
    this._cookieUpdateTimeout = setTimeout(async () => {
      await this._handleCookieUpdate(newCookie);
      this.setState({ cookie: newCookie });
    }, DEBOUNCE_MILLIS * 2);
  }

  static _capitalize(str: string) {
    return str.charAt(0).toUpperCase() + str.slice(1);
  }

  _getRawCookieString() {
    const { cookie } = this.state;

    if (!cookie) {
      return '';
    }

    try {
      return cookieToString(toughCookie.Cookie.fromJSON(JSON.stringify(cookie)));
    } catch (err) {
      console.warn('Failed to parse cookie string', err);
      return '';
    }
  }

  _renderInputField(field: string, error: string | null = null) {
    const { cookie } = this.state;
    const {
      handleRender,
      handleGetRenderContext,
      nunjucksPowerUserMode,
      isVariableUncovered,
    } = this.props;

    if (!cookie) {
      return null;
    }

    const val = (cookie[field] || '').toString();

    return (

          {CookieModifyModal._capitalize(field)} {error}
          <OneLineEditor
            render={handleRender}
            getRenderContext={handleGetRenderContext}
            nunjucksPowerUserMode={nunjucksPowerUserMode}
            isVariableUncovered={isVariableUncovered}
            defaultValue={val || ''}
            onChange={value => this._handleChange(field, value)}
          />

    );
  }

  render() {
    const { cookieJar } = this.props;
    const { cookie } = this.state;
    const checkFields = ['secure', 'httpOnly'];

    return (
      <Modal ref={this._setModalRef} {...(this.props: Object)}>
        <ModalHeader>Edit Cookie</ModalHeader>
        <ModalBody className="cookie-modify">
          {cookieJar && cookie && (
            <Tabs>
              <TabList>
                <Tab tabIndex="-1">
                  <button>Friendly</button>
                </Tab>
                <Tab tabIndex="-1">
                  <button>Raw</button>
                </Tab>
              </TabList>
              <TabPanel>

                    {this._renderInputField('key')}
                    {this._renderInputField('value')}

                    {this._renderInputField('domain')}
                    {this._renderInputField('path')}

                  {this._renderInputField(
                    'expires',
                    isNaN(new Date(cookie.expires || 0).getTime()) ? 'Invalid Date' : null,
                  )}

                  {checkFields.map((field, i) => {
                    const checked = !!cookie[field];

                    return (

                        {CookieModifyModal._capitalize(field)}
                        <input
                          className="space-left"
                          type="checkbox"
                          name={field}
                          defaultChecked={checked || false}
                          onChange={e => this._handleChange(field, e)}
                        />

                    );
                  })}

              </TabPanel>
              <TabPanel className="react-tabs__tab-panel pad">

                    Raw Cookie String
                    <input
                      type="text"
                      onChange={this._handleChangeRawValue}
                      defaultValue={this._getRawCookieString()}
                    />

              </TabPanel>
            </Tabs>
          )}
        </ModalBody>
        <ModalFooter>
          <button className="btn" onClick={this.hide}>
            Done
          </button>
        </ModalFooter>
      </Modal>
    );
  }
}

// export CookieModifyModal;
export default CookieModifyModal;


"private": true,
"version": "2.2.1",
"main": "index.js",
"insomnia": {
"name": "kong-themes"
}
}

"name": "kong-themes",
"core": true,
"unlisted": true

insomnia-plugin-jsonpath

Template tag to pull data from JSON strings

json-path

Template tag to pull data out of JSON strings