Merge All Repositories into Monorepo for easier maintenance (#629)

* All projects into monorepo

* Update CI

* More CI updates

* Extracted a bunch of things into packages

* Publish

 - insomnia-plugin-base64@1.0.1
 - insomnia-plugin-default-headers@1.0.2
 - insomnia-plugin-file@1.0.1
 - insomnia-plugin-hash@1.0.1
 - insomnia-plugin-now@1.0.1
 - insomnia-plugin-request@1.0.1
 - insomnia-plugin-response@1.0.1
 - insomnia-plugin-uuid@1.0.1
 - insomnia-cookies@0.0.2
 - insomnia-importers@1.5.2
 - insomnia-prettify@0.0.3
 - insomnia-url@0.0.2
 - insomnia-xpath@0.0.2

* A bunch of small fixes

* Improved build script

* Fixed

* Merge dangling files

* Usability refactor

* Handle duplicate plugin names
This commit is contained in:
Gregory Schier
2017-11-26 20:45:40 +00:00
committed by GitHub
parent c79d7fdd77
commit 549ce23ce8
957 changed files with 44840 additions and 19355 deletions

View File

@@ -20,7 +20,7 @@ install:
- npm --version
- npm config set msvs_version 2013
- npm install 7zip-bin-win > NUL
- npm install > NUL
- npm run bootstrap
- npm test
cache:
@@ -32,14 +32,14 @@ cache:
#---------------------------------#
build_script:
- if %APPVEYOR_REPO_TAG%==true npm run build-n-package
- if %APPVEYOR_REPO_TAG%==true npm run package-app
#---------------------------------#
# artifacts configuration #
#---------------------------------#
artifacts:
- path: dist\squirrel-windows\*
- path: packages\insomnia-app\dist\squirrel-windows\*
name: dist
#---------------------------------#

33
.gitignore vendored
View File

@@ -1,27 +1,22 @@
node_modules
.idea
.vscode
dist/*
build/*
.DS_Store
coverage
# Generated
app/main.min.js
app/node_modules
# Logs
logs
*.log
npm-debug.log*
# Runtime data
yarn-debug.log*
yarn-error.log*
pids
*.pid
*.seed
# Optional npm cache directory
*.pid.lock
lib-cov
coverage
.lock-wscript
build/Release
node_modules/
.npm
# Optional REPL history
.eslintcache
.node_repl_history
*.tgz
.yarn-integrity
.env
.idea
.DS_Store

View File

@@ -35,16 +35,14 @@ cache:
- $HOME/.electron
- $HOME/.cache
install:
- npm install > /dev/null
script:
- node --version
- npm --version
script:
- npm run bootstrap
- npm test
before_deploy:
- npm run build-n-package
- npm run package-app
deploy:
provider: releases
@@ -53,10 +51,10 @@ deploy:
file_glob: true
prerelease: true
file:
- dist/**/*.zip
- dist/**/*.dmg
- dist/**/*.deb
- dist/**/*.AppImage
- dist/**/*.tar.gz
- packages/insomnia-app/dist/**/*.zip
- packages/insomnia-app/dist/**/*.dmg
- packages/insomnia-app/dist/**/*.deb
- packages/insomnia-app/dist/**/*.AppImage
- packages/insomnia-app/dist/**/*.tar.gz
on:
tags: true

View File

@@ -2,8 +2,6 @@
[![Insomnia](https://img.shields.io/badge/maintainer-Insomnia-purple.svg?colorB=6e60cc)](https://insomnia.rest)
[![Travis](https://api.travis-ci.org/getinsomnia/insomnia.svg)](https://travis-ci.org/getinsomnia/insomnia)
[![AppVeyor](https://img.shields.io/appveyor/ci/gschier/insomnia.svg)](https://ci.appveyor.com/project/gschier/insomnia)
[![license](https://img.shields.io/github/license/getinsomnia/insomnia.svg)](LICENSE)
[![Slack Channel](https://chat.insomnia.rest/badge.svg)](https://chat.insomnia.rest/)
[![Twitter Follow](https://img.shields.io/twitter/follow/getinsomnia.svg?style=social&label=%40GetInsomnia%20on%20Twitter&style=plastic)](https://twitter.com/getinsomnia)
@@ -43,16 +41,20 @@ Development on Insomnia can be done on Mac, Windows, or Linux as long as you hav
<details>
<summary>Initial Dev Setup</summary>
This repository is structured as a monorepo and contains many Node.JS packages. Each package has
it's own set of command, but the most common commands are available from the
root `[package.json](package.json)` adn can be accessed using the `npm run ...` command. Here
are the only three commands you should need to start developing on the app.
```bash
# Install dependencies and build add-ons for Electron
npm install
npm run rebuild
# Install and Link Dependencies
npm run bootstrap
# Start app
npm run dev
# Run tests
# Run Tests
npm test
# Start App with Live Reload
npm run start-app
```
</details>
@@ -83,7 +85,3 @@ Here is a list of plugins available for installation via NPM.
[Swaggymnia](https://github.com/mlabouardy/swaggymnia)  Generate [Swagger](https://swagger.io/) documentation for your existing API in Insomnia.
## License
[GNU AGPLv3](LICENSE) &copy; [Insomnia](https://insomnia.rest)

View File

@@ -1,146 +0,0 @@
import {CookieJar} from 'tough-cookie';
import * as cookieUtils from '../cookies';
import {globalBeforeEach} from '../../__jest__/before-each';
describe('jarFromCookies()', () => {
beforeEach(globalBeforeEach);
it('returns valid cookies', done => {
const jar = cookieUtils.jarFromCookies([{
key: 'foo',
value: 'bar',
domain: 'google.com'
}]);
jar.store.getAllCookies((err, cookies) => {
expect(err).toBeNull();
expect(cookies[0].domain).toEqual('google.com');
expect(cookies[0].key).toEqual('foo');
expect(cookies[0].value).toEqual('bar');
expect(cookies[0].creation instanceof Date).toEqual(true);
expect(cookies[0].expires).toEqual('Infinity');
done();
});
});
it('handles malformed JSON', () => {
const jar = cookieUtils.jarFromCookies('not a jar');
expect(jar.constructor.name).toBe('CookieJar');
});
});
describe('cookiesFromJar()', () => {
beforeEach(globalBeforeEach);
it('returns valid jar', async () => {
const d = new Date();
const initialCookies = [{
key: 'bar',
value: 'baz',
domain: 'insomnia.rest',
expires: d
}, {
// This one will fail to parse, and be skipped
bad: 'cookie'
}];
const jar = CookieJar.fromJSON({cookies: initialCookies});
const cookies = await cookieUtils.cookiesFromJar(jar);
expect(cookies[0].domain).toBe('insomnia.rest');
expect(cookies[0].key).toBe('bar');
expect(cookies[0].value).toBe('baz');
expect(cookies[0].creation).toMatch(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/);
expect(cookies[0].expires).toEqual(d.toISOString());
});
it('handles bad jar', async () => {
const jar = CookieJar.fromJSON({cookies: []});
// MemoryStore never actually throws errors, so lets mock the
// function to force it to this time.
jar.store.getAllCookies = cb => cb(new Error('Dummy Error'));
const cookies = await cookieUtils.cookiesFromJar(jar);
// Cookies failed to p
expect(cookies.length).toBe(0);
});
});
describe('cookieHeaderValueForUri()', () => {
beforeEach(globalBeforeEach);
it('gets cookies for valid case', async () => {
const jar = cookieUtils.jarFromCookies([{
key: 'foo',
value: 'bar',
path: '/',
domain: 'google.com'
}, {
key: 'foo',
value: 'inner',
path: '/inner',
domain: 'google.com'
}]);
expect(await cookieUtils.cookieHeaderValueForUri(jar, 'https://google.com/foo/bar'))
.toBe('foo=bar');
expect(await cookieUtils.cookieHeaderValueForUri(jar, 'https://insomnia.rest/'))
.toBe('');
expect(await cookieUtils.cookieHeaderValueForUri(jar, 'https://google.com/inner'))
.toBe('foo=inner; foo=bar');
});
it('handles errors properly', async () => {
const jar = cookieUtils.jarFromCookies([]);
jar.getCookies = (uri, cb) => cb(new Error('Dummy error'));
expect(
await cookieUtils.cookieHeaderValueForUri(jar, 'https://google.com')
).toBe('');
});
describe('cookieToString()', () => {
beforeEach(globalBeforeEach);
it('does its thing', async () => {
const jar = cookieUtils.jarFromCookies([{
key: 'foo',
value: 'bar',
path: '/',
domain: 'google.com'
}, {
key: 'foo1',
value: 'bar',
path: '/',
domain: 'google.com',
hostOnly: true
}, {
key: 'foo2',
value: 'bar',
path: '/',
domain: 'google.com',
hostOnly: false
}, {
key: 'foo3',
value: 'bar',
path: '/somepath'
}]);
const cookies = await cookieUtils.cookiesFromJar(jar);
expect(cookies.length).toBe(4);
expect(cookieUtils.cookieToString(cookies[0]))
.toBe('foo=bar; Domain=google.com; Path=/');
expect(cookieUtils.cookieToString(cookies[1]))
.toBe('foo1=bar; Path=/; Domain=google.com');
expect(cookieUtils.cookieToString(cookies[2]))
.toBe('foo2=bar; Domain=google.com; Path=/');
expect(cookieUtils.cookieToString(cookies[3]))
.toBe('foo3=bar; Path=/somepath');
});
});
});

View File

@@ -1,234 +0,0 @@
import * as misc from '../misc';
import {globalBeforeEach} from '../../__jest__/before-each';
describe('getBasicAuthHeader()', () => {
beforeEach(globalBeforeEach);
it('succeed with username and password', () => {
const header = misc.getBasicAuthHeader('user', 'password');
expect(header).toEqual({
name: 'Authorization',
value: 'Basic dXNlcjpwYXNzd29yZA=='
});
});
it('succeed with no username', () => {
const header = misc.getBasicAuthHeader(null, 'password');
expect(header).toEqual({
name: 'Authorization',
value: 'Basic OnBhc3N3b3Jk'
});
});
it('succeed with username and empty password', () => {
const header = misc.getBasicAuthHeader('user', '');
expect(header).toEqual({
name: 'Authorization',
value: 'Basic dXNlcjo='
});
});
it('succeed with username and null password', () => {
const header = misc.getBasicAuthHeader('user', null);
expect(header).toEqual({
name: 'Authorization',
value: 'Basic dXNlcjo='
});
});
});
describe('hasAuthHeader()', () => {
beforeEach(globalBeforeEach);
it('finds valid header', () => {
const yes = misc.hasAuthHeader([
{name: 'foo', value: 'bar'},
{name: 'authorization', value: 'foo'}
]);
expect(yes).toEqual(true);
});
it('finds valid header case insensitive', () => {
const yes = misc.hasAuthHeader([
{name: 'foo', value: 'bar'},
{name: 'AuthOrizAtiOn', value: 'foo'}
]);
expect(yes).toEqual(true);
});
});
describe('generateId()', () => {
beforeEach(globalBeforeEach);
it('generates a valid ID', () => {
const id = misc.generateId('foo');
expect(id).toMatch(/^foo_[a-z0-9]{32}$/);
});
it('generates without prefix', () => {
const id = misc.generateId();
expect(id).toMatch(/^[a-z0-9]{32}$/);
});
});
describe('setDefaultProtocol()', () => {
beforeEach(globalBeforeEach);
it('no-ops on empty url', () => {
const url = misc.setDefaultProtocol('');
expect(url).toBe('');
});
it('correctly sets protocol for empty', () => {
const url = misc.setDefaultProtocol('google.com');
expect(url).toBe('http://google.com');
});
it('does not set for valid url', () => {
const url = misc.setDefaultProtocol('https://google.com');
expect(url).toBe('https://google.com');
});
it('does not set for valid url', () => {
const url = misc.setDefaultProtocol('http://google.com');
expect(url).toBe('http://google.com');
});
it('does not set for invalid url', () => {
const url = misc.setDefaultProtocol('httbad://google.com');
expect(url).toBe('httbad://google.com');
});
});
describe('prepareUrlForSending()', () => {
beforeEach(globalBeforeEach);
it('does not touch normal url', () => {
const url = misc.prepareUrlForSending('http://google.com');
expect(url).toBe('http://google.com/');
});
it('works with no protocol', () => {
const url = misc.prepareUrlForSending('google.com');
expect(url).toBe('http://google.com/');
});
it('encodes pathname', () => {
const url = misc.prepareUrlForSending('https://google.com/foo bar/100%/foo');
expect(url).toBe('https://google.com/foo%20bar/100%25/foo');
});
it('encodes pathname mixed encoding', () => {
const url = misc.prepareUrlForSending('https://google.com/foo bar baz%20qux/100%/foo%25');
expect(url).toBe('https://google.com/foo%20bar%20baz%20qux/100%25/foo%25');
});
it('leaves already encoded pathname', () => {
const url = misc.prepareUrlForSending('https://google.com/foo%20bar%20baz/100%25/foo');
expect(url).toBe('https://google.com/foo%20bar%20baz/100%25/foo');
});
it('encodes querystring', () => {
const url = misc.prepareUrlForSending('https://google.com?s=foo bar 100%&hi');
expect(url).toBe('https://google.com/?s=foo%20bar%20100%25&hi');
});
it('encodes querystring with mixed spaces', () => {
const url = misc.prepareUrlForSending('https://google.com?s=foo %20100%');
expect(url).toBe('https://google.com/?s=foo%20%20100%25');
});
it('encodes querystring with repeated keys', () => {
const url = misc.prepareUrlForSending('https://google.com/;@,!?s=foo,;@-!&s=foo %20100%');
expect(url).toBe('https://google.com/;@,!?s=foo,%3B%40-!&s=foo%20%20100%25');
});
it('doesn\'t decode ignored characters', () => {
// Encoded should skip raw versions of @ ; ,
const url = misc.prepareUrlForSending('https://google.com/@;,&^+');
expect(url).toBe('https://google.com/@;,%26%5E+');
// Encoded should skip encoded versions of @ ; ,
const url2 = misc.prepareUrlForSending('https://google.com/%40%3B%2C%26%5E');
expect(url2).toBe('https://google.com/%40%3B%2C%26%5E');
});
it('leaves already encoded characters alone', () => {
const url = misc.prepareUrlForSending('https://google.com/%2B%2A%2F>');
expect(url).toBe('https://google.com/%2B%2A%2F%3E');
});
it('doesn\'t encode if last param set', () => {
const url = misc.prepareUrlForSending('https://google.com/%%?foo=%%', false);
expect(url).toBe('https://google.com/%%?foo=%%');
});
});
describe('filterHeaders()', () => {
beforeEach(globalBeforeEach);
it('handles bad headers', () => {
expect(misc.filterHeaders(null, null)).toEqual([]);
expect(misc.filterHeaders([], null)).toEqual([]);
expect(misc.filterHeaders(['bad'], null)).toEqual([]);
expect(misc.filterHeaders(['bad'], 'good')).toEqual([]);
expect(misc.filterHeaders(null, 'good')).toEqual([]);
expect(misc.filterHeaders([{name: 'good', value: 'valid'}], null)).toEqual([]);
expect(misc.filterHeaders([{name: 'good', value: 'valid'}], 'good'))
.toEqual([{name: 'good', value: 'valid'}]);
});
});
describe('keyedDebounce()', () => {
beforeEach(async () => {
await globalBeforeEach();
jest.useFakeTimers();
});
it('debounces correctly', () => {
const resultsList = [];
const fn = misc.keyedDebounce(results => {
resultsList.push(results);
}, 100);
fn('foo', 'bar');
fn('baz', 'bar');
fn('foo', 'bar2');
fn('foo', 'bar3');
fn('multi', 'foo', 'bar', 'baz');
expect(setTimeout.mock.calls.length).toBe(5);
expect(resultsList).toEqual([]);
jest.runAllTimers();
expect(resultsList).toEqual([{
foo: ['bar3'],
baz: ['bar'],
multi: ['foo', 'bar', 'baz']
}]);
});
});
describe('debounce()', () => {
beforeEach(async () => {
await globalBeforeEach();
jest.useFakeTimers();
});
it('debounces correctly', () => {
const resultList = [];
const fn = misc.debounce((...args) => {
resultList.push(args);
}, 100);
fn('foo');
fn('foo');
fn('multi', 'foo', 'bar', 'baz');
fn('baz', 'bar');
fn('foo', 'bar3');
expect(setTimeout.mock.calls.length).toBe(5);
expect(resultList).toEqual([]);
jest.runAllTimers();
expect(resultList).toEqual([['foo', 'bar3']]);
});
});

View File

@@ -1,176 +0,0 @@
import * as querystringUtils from '../querystring';
import {globalBeforeEach} from '../../__jest__/before-each';
describe('getBasicAuthHeader()', () => {
beforeEach(globalBeforeEach);
it('gets joiner for bare URL', () => {
const joiner = querystringUtils.getJoiner('http://google.com');
expect(joiner).toBe('?');
});
it('gets joiner for invalid URL', () => {
const joiner = querystringUtils.getJoiner('hi');
expect(joiner).toBe('?');
});
it('gets joiner for URL with question mark', () => {
const joiner = querystringUtils.getJoiner('http://google.com?');
expect(joiner).toBe('&');
});
it('gets joiner for URL with params', () => {
const joiner = querystringUtils.getJoiner('http://google.com?foo=bar');
expect(joiner).toBe('&');
});
it('gets joiner for URL with hash', () => {
const joiner = querystringUtils.getJoiner('http://google.com?foo=bar#hi');
expect(joiner).toBe('&');
});
it('gets joiner for URL with ampersand', () => {
const joiner = querystringUtils.getJoiner(
'http://google.com?foo=bar&baz=qux'
);
expect(joiner).toBe('&');
});
});
describe('joinUrl()', () => {
beforeEach(globalBeforeEach);
it('joins bare URL', () => {
const url = querystringUtils.joinUrl(
'http://google.com',
'foo=bar'
);
expect(url).toBe('http://google.com?foo=bar');
});
it('joins with hash', () => {
const url = querystringUtils.joinUrl(
'http://google.com#hash',
'foo=bar'
);
expect(url).toBe('http://google.com?foo=bar#hash');
});
it('joins hash and querystring', () => {
const url = querystringUtils.joinUrl(
'http://google.com?baz=qux#hash',
'foo=bar'
);
expect(url).toBe('http://google.com?baz=qux&foo=bar#hash');
});
it('joins multi-hash and querystring', () => {
const url = querystringUtils.joinUrl(
'http://google.com?hi=there&baz=qux#hash#hi#hi',
'foo=bar'
);
expect(url).toBe('http://google.com?hi=there&baz=qux&foo=bar#hash#hi#hi');
});
it('joins URL with querystring', () => {
const url = querystringUtils.joinUrl(
'http://google.com?hi=there',
'foo=bar%20baz'
);
expect(url).toBe('http://google.com?hi=there&foo=bar%20baz');
});
});
describe('build()', () => {
beforeEach(globalBeforeEach);
it('builds simple param', () => {
const str = querystringUtils.build({name: 'foo', value: 'bar??'});
expect(str).toBe('foo=bar%3F%3F');
});
it('builds param without value', () => {
const str = querystringUtils.build({name: 'foo'});
expect(str).toBe('foo');
});
it('builds empty param without name', () => {
const str = querystringUtils.build({value: 'bar'});
expect(str).toBe('');
});
it('builds with numbers', () => {
const str = querystringUtils.build({name: 'number', value: 10});
const str2 = querystringUtils.build({name: 'number', value: 0});
expect(str).toBe('number=10');
expect(str2).toBe('number=0');
});
});
describe('buildFromParams()', () => {
beforeEach(globalBeforeEach);
it('builds from params', () => {
const str = querystringUtils.buildFromParams([
{name: 'foo', value: 'bar??'},
{name: 'hello'},
{name: 'hi there', value: 'bar??'},
{name: '', value: 'bar??'},
{name: '', value: ''}
]);
expect(str).toBe('foo=bar%3F%3F&hello&hi%20there=bar%3F%3F');
});
it('builds from params', () => {
const str = querystringUtils.buildFromParams([
{name: 'foo', value: 'bar??'},
{name: 'hello'},
{name: 'hi there', value: 'bar??'},
{name: '', value: 'bar??'},
{name: '', value: ''}
], false);
expect(str).toBe('foo=bar%3F%3F&hello=&hi%20there=bar%3F%3F&=bar%3F%3F&=');
});
});
describe('deconstructToParams()', () => {
beforeEach(globalBeforeEach);
it('builds from params', () => {
const str = querystringUtils.deconstructToParams(
'foo=bar%3F%3F&hello&hi%20there=bar%3F%3F&=&=val'
);
expect(str).toEqual([
{name: 'foo', value: 'bar??'},
{name: 'hello', value: ''},
{name: 'hi there', value: 'bar??'}
]);
});
it('builds from params with =', () => {
const str = querystringUtils.deconstructToParams(
'foo=bar&1=2=3=4&hi'
);
expect(str).toEqual([
{name: 'foo', value: 'bar'},
{name: '1', value: '2=3=4'},
{name: 'hi', value: ''}
]);
});
});
describe('deconstructToParams()', () => {
beforeEach(globalBeforeEach);
it('builds from params not strict', () => {
const str = querystringUtils.deconstructToParams(
'foo=bar%3F%3F&hello&hi%20there=bar%3F%3F&=&=val',
false
);
expect(str).toEqual([
{name: 'foo', value: 'bar??'},
{name: 'hello', value: ''},
{name: 'hi there', value: 'bar??'},
{name: '', value: ''},
{name: '', value: 'val'}
]);
});
});

View File

@@ -1,138 +0,0 @@
// @flow
import * as util from './misc.js';
type Parameter = Object & {
name: string,
value: string
}
/** Join a URL with a querystring */
export function joinUrl (url: string, qs: string): string {
if (!qs) {
return url;
}
if (!url) {
return qs;
}
const [base, ...hashes] = url.split('#');
// TODO: Make this work with URLs that have a #hash component
const baseUrl = base || '';
const joiner = getJoiner(base);
const hash = hashes.length ? `#${hashes.join('#')}` : '';
return `${baseUrl}${joiner}${qs}${hash}`;
}
export function extractFromUrl (url: string): string {
if (!url) {
return '';
}
// NOTE: This only splits on first ? sign. '1=2=3' --> ['1', '2=3']
const things = url.split('?');
if (things.length === 1) {
return '';
} else {
const qsWithHash = things.slice(1).join('?');
return qsWithHash.replace(/#.*/, '');
}
}
export function getJoiner (url: string): string {
url = url || '';
return url.indexOf('?') === -1 ? '?' : '&';
}
/** Build a querystring param out of a name/value pair */
export function build (param: Parameter, strict: boolean = true): string {
// Skip non-name ones in strict mode
if (strict && !param.name) {
return '';
}
// Cast number values to strings
if (typeof param.value === 'number') {
param.value += '';
}
if (!strict || param.value) {
// Don't encode ',' in values
const value = util.flexibleEncodeComponent(param.value || '').replace(/%2C/gi, ',');
const name = util.flexibleEncodeComponent(param.name || '');
return `${name}=${value}`;
} else {
return util.flexibleEncodeComponent(param.name);
}
}
/**
* Build a querystring from a list of name/value pairs
* @param parameters
* @param strict allow empty names and values
*/
export function buildFromParams (
parameters: Array<Parameter>,
strict: boolean = true
): string {
let items = [];
for (const param of parameters) {
let built = build(param, strict);
if (!built) {
continue;
}
items.push(built);
}
return items.join('&');
}
/**
* Deconstruct a querystring to name/value pairs
* @param qs
* @param strict allow empty names and values
*/
export function deconstructToParams (qs: ?string, strict: boolean = true): Array<Parameter> {
const pairs: Array<Parameter> = [];
if (!qs) {
return pairs;
}
const stringPairs = qs.split('&');
for (let stringPair of stringPairs) {
// NOTE: This only splits on first equals sign. '1=2=3' --> ['1', '2=3']
const [encodedName, ...encodedValues] = stringPair.split('=');
const encodedValue = encodedValues.join('=');
let name = '';
try {
name = decodeURIComponent(encodedName || '');
} catch (e) {
// Just leave it
name = encodedName;
}
let value = '';
try {
value = decodeURIComponent(encodedValue || '');
} catch (e) {
// Just leave it
value = encodedValue;
}
if (strict && !name) {
continue;
}
pairs.push({name, value});
}
return pairs;
}

2454
app/package-lock.json generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,39 +0,0 @@
{
"private": true,
"name": "insomnia",
"version": "5.12.0-beta.3",
"productName": "Insomnia",
"longName": "Insomnia REST Client",
"description": "Debug APIs like a human, not a robot",
"homepage": "https://insomnia.rest",
"author": "Insomnia <support@insomnia.rest>",
"main": "main.min.js",
"dependencies": {
"aws4": "^1.6.0",
"clone": "^2.1.0",
"deep-equal": "^1.0.1",
"electron-context-menu": "^0.9.0",
"electron-squirrel-startup": "^1.0.0",
"hawk": "^6.0.2",
"hkdf": "^0.0.2",
"html-entities": "^1.2.0",
"iconv-lite": "^0.4.15",
"insomnia-httpsnippet": "^1.16.5",
"insomnia-importers": "^1.5.0",
"insomnia-node-libcurl": "^1.2.3",
"jsonpath": "^0.2.12",
"marked": "^0.3.6",
"mime-types": "^2.1.14",
"mkdirp": "^0.5.1",
"moment": "^2.18.1",
"nedb": "^1.8.0",
"node-forge": "^0.7.0",
"oauth-1.0a": "^2.2.2",
"srp-js": "^0.2.0",
"tar": "^3.1.7",
"tough-cookie": "^2.3.1",
"vkbeautify": "^0.99.1",
"whatwg-fetch": "^2.0.1",
"xmldom": "^0.1.22"
}
}

View File

@@ -1,32 +0,0 @@
import * as templating from '../../index';
import {globalBeforeEach} from '../../../__jest__/before-each';
function assertTemplate (txt, expected) {
return async function () {
const result = await templating.render(txt);
expect(result).toMatch(expected);
};
}
function assertTemplateFails (txt, expected) {
return async function () {
try {
await templating.render(txt);
fail(`Render should have thrown ${expected}`);
} catch (err) {
expect(err.message).toBe(expected);
}
};
}
describe('Base64EncodeExtension', () => {
beforeEach(globalBeforeEach);
it('encodes nothing', assertTemplate("{% base64 'encode' %}", ''));
it('encodes something', assertTemplate("{% base64 'encode', 'my string' %}", 'bXkgc3RyaW5n'));
it('decodes nothing', assertTemplate("{% base64 'decode' %}", ''));
it('decodes something', assertTemplate("{% base64 'decode', 'bXkgc3RyaW5n' %}", 'my string'));
it('fails on invalid op', assertTemplateFails(
"{% base64 'foo' %}",
'Unsupported operation "foo". Must be encode or decode.'
));
});

View File

@@ -1,32 +0,0 @@
import path from 'path';
import * as templating from '../../index';
import {globalBeforeEach} from '../../../__jest__/before-each';
function assertTemplate (txt, context, expected) {
return async function () {
const result = await templating.render(txt, {context});
expect(result).toBe(expected);
};
}
function assertTemplateFails (txt, context, expected) {
return async function () {
try {
await templating.render(txt, {context});
fail(`Render should have thrown ${expected}`);
} catch (err) {
expect(err.message).toContain(expected);
}
};
}
describe('FileExtension', () => {
beforeEach(globalBeforeEach);
const ctx = {path: path.resolve(__dirname, path.join('./test.txt'))};
const escapedPath = ctx.path.replace(/\\/g, '\\\\');
it('reads from string', assertTemplate(`{% file "${escapedPath}" %}`, ctx, 'Hello World!'));
it('reads a file correctly', assertTemplate('{% file path %}', ctx, 'Hello World!'));
it('fails on missing file', assertTemplateFails('{% file "/foo" %}', ctx, `ENOENT: no such file or directory, open '${path.resolve('/foo')}'`));
it('fails on no 2nd param', assertTemplateFails('{% file %}', ctx, 'No file selected'));
it('fails on unknown variable', assertTemplateFails('{% file foo %}', ctx, 'No file selected'));
});

View File

@@ -1,36 +0,0 @@
import * as templating from '../../index';
import {globalBeforeEach} from '../../../__jest__/before-each';
function assertTemplate (txt, expected) {
return async function () {
const result = await templating.render(txt);
expect(result).toMatch(expected);
};
}
function assertTemplateFails (txt, expected) {
return async function () {
try {
await templating.render(txt);
fail(`Render should have thrown ${expected}`);
} catch (err) {
expect(err.message).toBe(expected);
}
};
}
const isoRe = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/;
const secondsRe = /^\d{10}$/;
const millisRe = /^\d{13}$/;
describe('NowExtension', () => {
beforeEach(globalBeforeEach);
it('renders default ISO', assertTemplate('{% now %}', isoRe));
it('renders ISO-8601', assertTemplate('{% now "ISO-8601" %}', isoRe));
it('renders seconds', assertTemplate('{% now "seconds" %}', secondsRe));
it('renders s', assertTemplate('{% now "s" %}', secondsRe));
it('renders unix', assertTemplate('{% now "unix" %}', secondsRe));
it('renders millis', assertTemplate('{% now "millis" %}', millisRe));
it('renders ms', assertTemplate('{% now "ms" %}', millisRe));
it('fails on other', assertTemplateFails('{% now "foo" %}', 'Invalid date type "foo"'));
});

View File

@@ -1,85 +0,0 @@
import * as templating from '../../index';
import * as models from '../../../models';
import {cookiesFromJar, jarFromCookies} from '../../../common/cookies';
import {getRenderContext} from '../../../common/render';
import {globalBeforeEach} from '../../../__jest__/before-each';
describe('RequestExtension cookie', async () => {
beforeEach(globalBeforeEach);
it('should get cookie by name', async () => {
// Create necessary models
const workspace = await models.workspace.create({name: 'Workspace'});
const request = await models.request.create({
parentId: workspace._id,
url: 'https://insomnia.rest/foo/bar'
});
const cookieJar = await models.cookieJar.getOrCreateForParentId(workspace._id);
const jar = jarFromCookies(cookieJar.cookies);
jar.setCookieSync([
'foo=bar',
'path=/',
'domain=.insomnia.rest',
'HttpOnly Cache-Control: public, no-cache'
].join('; '), request.url);
const cookies = await cookiesFromJar(jar);
await models.cookieJar.update(cookieJar, {cookies});
const context = await getRenderContext(request);
const result = await templating.render(`{% request 'cookie', 'foo' %}`, {context});
expect(result).toBe('bar');
});
});
describe('RequestExtension url', async () => {
beforeEach(globalBeforeEach);
it('should get url', async () => {
// Create necessary models
const workspace = await models.workspace.create({name: 'Workspace'});
const request = await models.request.create({
parentId: workspace._id,
url: 'https://insomnia.rest/foo/bar',
parameters: [{name: 'foo', value: 'bar'}]
});
const context = await getRenderContext(request);
const result = await templating.render(`{% request 'url' %}`, {context});
expect(result).toBe('https://insomnia.rest/foo/bar?foo=bar');
});
it('should get rendered url', async () => {
// Create necessary models
const workspace = await models.workspace.create({name: 'Workspace'});
const request = await models.request.create({
parentId: workspace._id,
url: 'https://insomnia.rest/foo/bar',
parameters: [{name: 'foo', value: '{{ foo }}'}]
});
const context = await getRenderContext(request);
context.foo = 'Hello World!';
const result = await templating.render(`{% request 'url' %}`, {context});
expect(result).toBe('https://insomnia.rest/foo/bar?foo=Hello%20World!');
});
});
describe('RequestExtension header', async () => {
beforeEach(globalBeforeEach);
it('should get url', async () => {
// Create necessary models
const workspace = await models.workspace.create({name: 'Workspace'});
const request = await models.request.create({
parentId: workspace._id,
url: 'https://insomnia.rest/foo/bar',
headers: [{name: 'foo', value: '{{ foo }}'}]
});
const context = await getRenderContext(request);
context.foo = 'Hello World!';
const result = await templating.render(`{% request 'header', 'foo' %}`, {context});
expect(result).toBe('Hello World!');
});
});

View File

@@ -1,273 +0,0 @@
import * as templating from '../../index';
import * as models from '../../../models';
import {globalBeforeEach} from '../../../__jest__/before-each';
describe('ResponseExtension General', async () => {
beforeEach(globalBeforeEach);
it('fails on no responses', async () => {
const request = await models.request.create({parentId: 'foo'});
try {
await templating.render(`{% response "body", "${request._id}", "$.foo" %}`);
fail('JSON should have failed to parse');
} catch (err) {
expect(err.message).toContain('No responses for request');
}
});
it('fails on no request', async () => {
await models.response.create({parentId: 'req_test'}, '{"foo": "bar"}');
try {
await templating.render(`{% response "body", "req_test", "$.foo" %}`);
fail('JSON should have failed to parse');
} catch (err) {
expect(err.message).toContain('Could not find request req_test');
}
});
it('fails on empty filter', async () => {
const request = await models.request.create({parentId: 'foo'});
await models.response.create({parentId: request._id, statusCode: 200}, '{"foo": "bar"}');
try {
await templating.render(`{% response "body", "${request._id}", "" %}`);
fail('Should have failed');
} catch (err) {
expect(err.message).toContain('No body filter specified');
}
});
});
describe('ResponseExtension JSONPath', async () => {
beforeEach(globalBeforeEach);
it('renders basic response "body", query', async () => {
const request = await models.request.create({parentId: 'foo'});
await models.response.create({
parentId: request._id,
body: '{"foo": "bar"}',
statusCode: 200
});
const result = await templating.render(`{% response "body", "${request._id}", "$.foo" %}`);
expect(result).toBe('bar');
});
it('fails on invalid JSON', async () => {
const request = await models.request.create({parentId: 'foo'});
await models.response.create({
parentId: request._id,
body: '{"foo": "bar"',
statusCode: 200
});
try {
await templating.render(`{% response "body", "${request._id}", "$.foo" %}`);
fail('JSON should have failed to parse');
} catch (err) {
expect(err.message).toContain('Invalid JSON: Unexpected end of JSON input');
}
});
it('fails on invalid query', async () => {
const request = await models.request.create({parentId: 'foo'});
await models.response.create({
parentId: request._id,
body: '{"foo": "bar"}',
statusCode: 200
});
try {
await templating.render(`{% response "body", "${request._id}", "$$" %}`);
fail('JSON should have failed to parse');
} catch (err) {
expect(err.message).toContain('Invalid JSONPath query: $$');
}
});
it('fails on no results', async () => {
const request = await models.request.create({parentId: 'foo'});
await models.response.create({
parentId: request._id,
body: '{"foo": "bar"}',
statusCode: 200
});
try {
await templating.render(`{% response "body", "${request._id}", "$.missing" %}`);
fail('JSON should have failed to parse');
} catch (err) {
expect(err.message).toContain('Returned no results: $.missing');
}
});
it('fails on more than 1 result', async () => {
const request = await models.request.create({parentId: 'foo'});
await models.response.create({
parentId: request._id,
body: '{"array": [1,2,3]}',
statusCode: 200
});
try {
await templating.render(`{% response "body", "${request._id}", "$.array.*" %}`);
fail('JSON should have failed to parse');
} catch (err) {
expect(err.message).toContain('Returned more than one result: $.array.*');
}
});
});
describe('ResponseExtension XPath', async () => {
beforeEach(globalBeforeEach);
it('renders basic response "body" query', async () => {
const request = await models.request.create({parentId: 'foo'});
await models.response.create({
parentId: request._id,
body: '<foo><bar>Hello World!</bar></foo>',
statusCode: 200
});
const result = await templating.render(`{% response "body", "${request._id}", "/foo/bar" %}`);
expect(result).toBe('Hello World!');
});
it('renders basic response "body" attribute query', async () => {
const request = await models.request.create({parentId: 'foo'});
await models.response.create({
parentId: request._id,
body: '<foo><bar hello="World!">Hello World!</bar></foo>',
statusCode: 200
});
const result = await templating.render(`{% response "body", "${request._id}", "/foo/bar/@hello" %}`);
expect(result).toBe('World!');
});
it('no results on invalid XML', async () => {
const request = await models.request.create({parentId: 'foo'});
await models.response.create({
parentId: request._id,
body: '<hi></hi></sstr>',
statusCode: 200
});
try {
await templating.render(`{% response "body", "${request._id}", "/foo" %}`);
fail('should have failed');
} catch (err) {
expect(err.message).toContain('Returned no results: /foo');
}
});
it('fails on invalid query', async () => {
const request = await models.request.create({parentId: 'foo'});
await models.response.create({
parentId: request._id,
body: '<foo><bar>Hello World!</bar></foo>',
statusCode: 200
});
try {
await templating.render(`{% response "body", "${request._id}", "//" %}`);
fail('should have failed');
} catch (err) {
expect(err.message).toContain('Invalid XPath query: //');
}
});
it('fails on no results', async () => {
const request = await models.request.create({parentId: 'foo'});
await models.response.create({
parentId: request._id,
body: '<foo><bar>Hello World!</bar></foo>',
statusCode: 200
});
try {
await templating.render(`{% response "body", "${request._id}", "/missing" %}`);
fail('should have failed');
} catch (err) {
expect(err.message).toContain('Returned no results: /missing');
}
});
it('fails on more than 1 result', async () => {
const request = await models.request.create({parentId: 'foo'});
await models.response.create({
parentId: request._id,
body: '<foo><bar>Hello World!</bar><bar>And again!</bar></foo>',
statusCode: 200
});
try {
await templating.render(`{% response "body", "${request._id}", "/foo/*" %}`);
fail('should have failed');
} catch (err) {
expect(err.message).toContain('Returned more than one result: /foo/*');
}
});
});
describe('ResponseExtension Header', async () => {
beforeEach(globalBeforeEach);
it('renders basic response "header"', async () => {
const request = await models.request.create({parentId: 'foo'});
await models.response.create({
parentId: request._id,
statusCode: 200,
headers: [
{name: 'Content-Type', value: 'application/json'},
{name: 'Content-Length', value: '20'}
]
});
const id = request._id;
expect(await templating.render(`{% response "header", "${id}", "content-type" %}`))
.toBe('application/json');
expect(await templating.render(`{% response "header", "${id}", "Content-Type" %}`))
.toBe('application/json');
expect(await templating.render(`{% response "header", "${id}", "CONTENT-type" %}`))
.toBe('application/json');
expect(await templating.render(`{% response "header", "${id}", " CONTENT-type " %}`))
.toBe('application/json');
});
it('no results on missing header', async () => {
const request = await models.request.create({parentId: 'foo'});
await models.response.create({
parentId: request._id,
statusCode: 200,
headers: [
{name: 'Content-Type', value: 'application/json'},
{name: 'Content-Length', value: '20'}
]
});
try {
await templating.render(`{% response "header", "${request._id}", "dne" %}`);
fail('should have failed');
} catch (err) {
expect(err.message).toBe('No header with name "dne".\nChoices are [\n\t"Content-Type",\n\t"Content-Length"\n]');
}
});
});
describe('ResponseExtension Raw', async () => {
beforeEach(globalBeforeEach);
it('renders basic response "header"', async () => {
const request = await models.request.create({parentId: 'foo'});
await models.response.create({
parentId: request._id,
body: 'Hello World!',
statusCode: 200
});
const result = await templating.render(`{% response "raw", "${request._id}", "" %}`);
expect(result).toBe('Hello World!');
});
});

View File

@@ -1,16 +0,0 @@
import * as templating from '../../index';
import {globalBeforeEach} from '../../../__jest__/before-each';
function assertTemplate (txt, expected) {
return async function () {
const result = await templating.render(txt);
expect(result).toMatch(expected);
};
}
const millisRe = /^\d{13}$/;
describe('TimestampExtension', () => {
beforeEach(globalBeforeEach);
it('renders basic', assertTemplate('{% timestamp %}', millisRe));
});

View File

@@ -1,32 +0,0 @@
import * as templating from '../../index';
import {globalBeforeEach} from '../../../__jest__/before-each';
function assertTemplate (txt, expected) {
return async function () {
const result = await templating.render(txt);
expect(result).toMatch(expected);
};
}
function assertTemplateFails (txt, expected) {
return async function () {
try {
await templating.render(txt);
fail(`Render should have thrown ${expected}`);
} catch (err) {
expect(err.message).toBe(expected);
}
};
}
describe('UuidExtension', () => {
beforeEach(globalBeforeEach);
it('renders default v4', assertTemplate('{% uuid %}', 'dd2ccc1a-2745-477a-881a-9e8ef9d42403'));
it('renders 4', assertTemplate('{% uuid "4" %}', 'e3e96e5f-dd68-4229-8b66-dee1f0940f3d'));
it('renders 4 num', assertTemplate('{% uuid 4 %}', 'a262d22b-5fa8-491c-9bd9-58fba03e301e'));
it('renders v4', assertTemplate('{% uuid "v4" %}', '2e7c2688-09ee-44b8-900d-5cbbaa7d3a19'));
it('renders 1', assertTemplate('{% uuid "1" %}', 'f7272c80-f493-11e6-bc64-92361f002671'));
it('renders 1 num', assertTemplate('{% uuid 1 %}', 'f7272f0a-f493-11e6-bc64-92361f002671'));
it('renders v1', assertTemplate('{% uuid "v1" %}', 'f72733a6-f493-11e6-bc64-92361f002671'));
it('fails on other', assertTemplateFails('{% uuid "foo" %}', 'Invalid UUID type "foo"'));
});

View File

@@ -1,9 +0,0 @@
export default {
deprecated: true,
name: 'timestamp',
displayName: 'Timestamp',
description: 'generate timestamp in milliseconds',
run (context) {
return Date.now();
}
};

17
lerna.json Normal file
View File

@@ -0,0 +1,17 @@
{
"lerna": "2.5.1",
"version": "independent",
"packages": [
"plugins/*",
"packages/*",
"website"
],
"commands": {
"publish": {
"ignore": [
"insomnia-app",
"insomnia-website"
]
}
}
}

9
netlify.toml Normal file
View File

@@ -0,0 +1,9 @@
[build]
publish = "website/_speedpack"
command = "npm run build-website"
[context.production.environment]
HUGO_VERSION = "0.30"
[context.deploy-preview.environment]
HUGO_VERSION = "0.30"

16976
package-lock.json generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,161 +1,25 @@
{
"name": "insomnia-dev",
"name": "insomnia",
"private": true,
"version": "1.0.0",
"description": "Insomnia App",
"cache": 3,
"main": "main.js",
"licence": "GNU AGPLv3",
"homepage": "https://insomnia.rest",
"repository": {
"type": "git",
"author": "Gregory Schier <gschier1990@gmail.com>",
"repository": "https://github.com/getinsomnia/insomnia",
"bugs": {
"url": "https://github.com/getinsomnia/insomnia"
},
"scripts": {
"test:lint": "eslint \"**/*.js\" \"**/*.html\" \"**/*.json\"",
"test:noisy": "cross-env NODE_ENV=test jest",
"test:coverage": "cross-env NODE_ENV=test jest --coverage --silent && open ./coverage/lcov-report/index.html",
"test:watch": "cross-env NODE_ENV=test jest --silent --watch",
"test:flow": "flow check",
"test": "npm run test:flow && npm run test:lint && cross-env NODE_ENV=test jest --silent --maxWorkers 1",
"start-hot": "npm run build-main && cross-env HOT=1 INSOMNIA_ENV=development electron app",
"build-main": "cross-env NODE_ENV=development webpack --config ./webpack/webpack.config.electron.babel.js",
"hot-server": "webpack-dev-server --config ./webpack/webpack.config.development.babel.js",
"dev": "concurrently --kill-others \"npm run hot-server\" \"npm run start-hot\"",
"rebuild": "electron-rebuild -f -w node-libcurl",
"package": "node ./scripts/package.js",
"build": "node ./scripts/build.js",
"build-n-package": "npm run build && npm run package"
},
"dev": {
"webpack-dev-server-port": 3333
},
"jest": {
"setupFiles": [
"./__jest__/setup.js"
],
"moduleNameMapper": {
"\\.(css|less|png)$": "<rootDir>/__mocks__/dummy.js",
"^worker-loader!": "<rootDir>/__mocks__/dummy.js"
},
"testMatch": [
"**/__tests__/**/*.test.js?(x)"
],
"rootDir": "app",
"verbose": false,
"resetMocks": true,
"resetModules": true,
"collectCoverage": false,
"collectCoverageFrom": [
"**/*.js",
"!**/*.min.js",
"!**/main.development.js",
"!**/static/**",
"!**/ui/components/**",
"!**/__fixtures__/**",
"!**/__tests__/**",
"!**/__jest__/**",
"!**/__mocks__/**",
"!**/node_modules/**"
]
},
"dependencies": {
"autobind-decorator": "^1.3.4",
"aws4": "^1.6.0",
"classnames": "^2.2.5",
"clone": "^2.1.0",
"codemirror": "^5.24.2",
"codemirror-graphql": "^0.6.11",
"deep-equal": "^1.0.1",
"electron-context-menu": "^0.9.0",
"electron-squirrel-startup": "^1.0.0",
"graphql": "^0.10.5",
"hawk": "^6.0.2",
"highlight.js": "^9.12.0",
"hkdf": "^0.0.2",
"html-entities": "^1.2.0",
"iconv-lite": "^0.4.15",
"insomnia-httpsnippet": "^1.16.5",
"insomnia-importers": "^1.5.0",
"insomnia-node-libcurl": "^1.2.3",
"jsonlint": "^1.6.2",
"jsonpath": "^0.2.12",
"jwt-authentication": "^0.3.3",
"marked": "^0.3.6",
"mime-types": "^2.1.14",
"mkdirp": "^0.5.1",
"moment": "^2.18.1",
"multiparty": "^4.1.3",
"nedb": "^1.8.0",
"node-forge": "^0.7.0",
"nunjucks": "^3.0.0",
"oauth-1.0a": "^2.2.2",
"pdfjs-dist": "^1.9.640",
"prop-types": "^15.5.10",
"react": "^16.0.0",
"react-dnd": "^2.4.0",
"react-dnd-html5-backend": "^2.4.1",
"react-dom": "^16.0.0",
"react-redux": "^5.0.6",
"react-tabs": "^2.1.1",
"redux": "^3.7.2",
"redux-thunk": "^2.2.0",
"reselect": "^3.0.1",
"srp-js": "^0.2.0",
"tar": "^3.1.7",
"tough-cookie": "^2.3.1",
"uuid": "^3.0.0",
"vkbeautify": "^0.99.1",
"whatwg-fetch": "^2.0.1",
"xmldom": "^0.1.22",
"xpath": "^0.0.23"
"bootstrap": "lerna bootstrap && lerna run rebuild",
"clean": "lerna run clean --stream",
"test": "lerna run test --stream",
"start-app": "lerna run start --stream --scope=insomnia-app",
"start-website": "lerna run start --stream --scope=insomnia-website",
"build-app": "lerna run build --stream --scope=insomnia-app",
"build-website": "lerna run build --stream --scope=insomnia-website",
"package-app": "lerna run package --stream --scope=insomnia-app"
},
"devDependencies": {
"babel-cli": "^6.23.0",
"babel-core": "^6.23.1",
"babel-eslint": "^7.1.1",
"babel-jest": "^19.0.0",
"babel-loader": "^7.1.2",
"babel-plugin-transform-decorators-legacy": "^1.3.4",
"babel-plugin-transform-object-rest-spread": "^6.23.0",
"babel-preset-env": "^1.6.0",
"babel-preset-flow": "^6.23.0",
"babel-preset-react": "^6.23.0",
"concurrently": "^3.5.0",
"cross-env": "^2.0.0",
"css-loader": "^0.28.7",
"electron": "^1.7.9",
"electron-builder": "^19.45.5",
"electron-builder-lib": "^19.46.3",
"electron-builder-squirrel-windows": "^19.46.0",
"electron-rebuild": "^1.6.0",
"eslint": "^3.16.1",
"eslint-config-semistandard": "^11.0.0",
"eslint-config-standard": "^10.2.1",
"eslint-plugin-filenames": "^1.1.0",
"eslint-plugin-flowtype": "^2.36.0",
"eslint-plugin-html": "^2.0.1",
"eslint-plugin-import": "^2.8.0",
"eslint-plugin-jest": "^19.0.1",
"eslint-plugin-json": "^1.2.0",
"eslint-plugin-node": "^5.2.1",
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-react": "^7.4.0",
"eslint-plugin-standard": "^3.0.1",
"file-loader": "^1.1.5",
"flow-bin": "^0.58.0",
"flow-bin": "^0.59.0",
"jest": "^21.2.1",
"less": "^2.7.2",
"less-loader": "^4.0.5",
"ncp": "^2.0.0",
"npm": "^5.5.1",
"react-hot-loader": "^3.1.3",
"redux-mock-store": "^1.0.2",
"rimraf": "^2.6.2",
"style-loader": "^0.19.0",
"url-loader": "^0.6.2",
"webpack": "^3.5.5",
"webpack-dev-server": "^2.9.4",
"webpack-target-electron-renderer": "^0.4.0"
"lerna": "^2.5.1"
}
}

View File

View File

@@ -0,0 +1,59 @@
{
"appId": "com.insomnia.app",
"publish": false,
"extraResources": [
{
"from": "./bin",
"to": "./bin",
"filter": "yarn-standalone.js"
}
],
"protocols": [
{
"name": "Insomnia",
"role": "Viewer",
"schemes": [
"insomnia"
]
}
],
"fileAssociations": [],
"directories": {
"app": "build",
"output": "dist"
},
"mac": {
"category": "public.app-category.developer-tools",
"target": [
"dmg",
"zip"
]
},
"win": {
"target": [
"squirrel",
"zip"
]
},
"linux": {
"executableName": "insomnia",
"synopsis": "A simple, beautiful, and free REST API client",
"category": "Development",
"target": [
"AppImage",
"deb",
"tar.gz"
]
},
"deb": {
"depends": [
"gconf2",
"gconf-service",
"libnotify4",
"libappindicator1",
"libxtst6",
"libnss3",
"libcurl3"
]
}
}

6
packages/insomnia-app/.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
dist
build
# Generated
app/main.min.js

View File

@@ -0,0 +1 @@
8

View File

View File

@@ -0,0 +1,3 @@
# Insomnia App
The main desktop application.

View File

@@ -17,6 +17,7 @@ const localStorageMock = (function () {
})();
// Don't console log during testing. It's annoying
global.__DEV__ = false;
global.console.log = () => null;
global.localStorage = localStorageMock;
global.requestAnimationFrame = cb => process.nextTick(cb);

View File

@@ -1,14 +1,12 @@
import * as appPackage from '../package.json';
import * as globalPackage from '../../package.json';
import {globalBeforeEach} from '../__jest__/before-each';
describe('package.json', () => {
beforeEach(globalBeforeEach);
it('all app dependencies should be same in global', () => {
for (const name of Object.keys(appPackage.dependencies)) {
const expected = globalPackage.dependencies[name];
const actual = appPackage.dependencies[name];
expect(`${name}::${actual}`).toBe(`${name}::${expected}`);
it('all packed dependencies should exist', () => {
for (const name of globalPackage.packedDependencies) {
const version = globalPackage.dependencies[name];
expect(version).toBeDefined();
}
});

View File

@@ -0,0 +1,108 @@
import * as misc from '../misc';
import {globalBeforeEach} from '../../__jest__/before-each';
describe('hasAuthHeader()', () => {
beforeEach(globalBeforeEach);
it('finds valid header', () => {
const yes = misc.hasAuthHeader([
{name: 'foo', value: 'bar'},
{name: 'authorization', value: 'foo'}
]);
expect(yes).toEqual(true);
});
it('finds valid header case insensitive', () => {
const yes = misc.hasAuthHeader([
{name: 'foo', value: 'bar'},
{name: 'AuthOrizAtiOn', value: 'foo'}
]);
expect(yes).toEqual(true);
});
});
describe('generateId()', () => {
beforeEach(globalBeforeEach);
it('generates a valid ID', () => {
const id = misc.generateId('foo');
expect(id).toMatch(/^foo_[a-z0-9]{32}$/);
});
it('generates without prefix', () => {
const id = misc.generateId();
expect(id).toMatch(/^[a-z0-9]{32}$/);
});
});
describe('filterHeaders()', () => {
beforeEach(globalBeforeEach);
it('handles bad headers', () => {
expect(misc.filterHeaders(null, null)).toEqual([]);
expect(misc.filterHeaders([], null)).toEqual([]);
expect(misc.filterHeaders(['bad'], null)).toEqual([]);
expect(misc.filterHeaders(['bad'], 'good')).toEqual([]);
expect(misc.filterHeaders(null, 'good')).toEqual([]);
expect(misc.filterHeaders([{name: 'good', value: 'valid'}], null)).toEqual([]);
expect(misc.filterHeaders([{name: 'good', value: 'valid'}], 'good'))
.toEqual([{name: 'good', value: 'valid'}]);
});
});
describe('keyedDebounce()', () => {
beforeEach(async () => {
await globalBeforeEach();
jest.useFakeTimers();
});
it('debounces correctly', () => {
const resultsList = [];
const fn = misc.keyedDebounce(results => {
resultsList.push(results);
}, 100);
fn('foo', 'bar');
fn('baz', 'bar');
fn('foo', 'bar2');
fn('foo', 'bar3');
fn('multi', 'foo', 'bar', 'baz');
expect(setTimeout.mock.calls.length).toBe(5);
expect(resultsList).toEqual([]);
jest.runAllTimers();
expect(resultsList).toEqual([{
foo: ['bar3'],
baz: ['bar'],
multi: ['foo', 'bar', 'baz']
}]);
});
});
describe('debounce()', () => {
beforeEach(async () => {
await globalBeforeEach();
jest.useFakeTimers();
});
it('debounces correctly', () => {
const resultList = [];
const fn = misc.debounce((...args) => {
resultList.push(args);
}, 100);
fn('foo');
fn('foo');
fn('multi', 'foo', 'bar', 'baz');
fn('baz', 'bar');
fn('foo', 'bar3');
expect(setTimeout.mock.calls.length).toBe(5);
expect(resultList).toEqual([]);
jest.runAllTimers();
expect(resultList).toEqual([['foo', 'bar3']]);
});
});

View File

@@ -1,11 +1,11 @@
// @flow
import * as models from '../models/index';
import {buildQueryStringFromParams, joinUrlAndQueryString} from 'insomnia-url';
import * as electron from 'electron';
import uuid from 'uuid';
import {GA_ID, GA_LOCATION, getAppPlatform, getAppVersion, isDevelopment} from './constants';
import {getAccountId} from '../sync/session';
import type {RequestParameter} from '../models/request';
import * as querystring from './querystring';
import {getScreenResolution, getUserLanguage, getViewportSize} from './misc';
const DIMENSION_PLATFORM = 1;
@@ -137,11 +137,11 @@ async function _sendToGoogle (params: Array<RequestParameter>) {
const baseParams = await _getDefaultParams();
const allParams = [...baseParams, ...params];
const qs = querystring.buildFromParams(allParams);
const qs = buildQueryStringFromParams(allParams);
const baseUrl = isDevelopment()
? 'https://www.google-analytics.com/debug/collect'
: 'https://www.google-analytics.com/collect';
const url = querystring.joinUrl(baseUrl, qs);
const url = joinUrlAndQueryString(baseUrl, qs);
const net = (electron.remote || electron).net;
const request = net.request(url);

View File

@@ -4,7 +4,7 @@ import {Cookie as toughCookie} from 'tough-cookie';
import * as models from '../models';
import {getRenderedRequest} from './render';
import type {RenderedRequest} from './render';
import {jarFromCookies} from './cookies';
import {jarFromCookies} from 'insomnia-cookies';
import * as misc from './misc';
import type {Cookie} from '../models/cookie-jar';
import {newBodyRaw} from '../models/request';
@@ -13,6 +13,7 @@ import type {Response as ResponseModel} from '../models/response';
import {getAuthHeader} from '../network/authentication';
import {getAppVersion} from './constants';
import {RenderError} from '../templating/index';
import {smartEncodeUrl} from 'insomnia-url';
export type HarCookie = {
name: string,
@@ -288,7 +289,7 @@ export async function exportHarWithRenderedRequest (
renderedRequest: RenderedRequest,
addContentLength: boolean = false
): Promise<HarRequest> {
const url = misc.prepareUrlForSending(renderedRequest.url, renderedRequest.settingEncodeUrl);
const url = smartEncodeUrl(renderedRequest.url, renderedRequest.settingEncodeUrl);
if (addContentLength) {
const hasContentLengthHeader = misc.filterHeaders(

View File

@@ -1,14 +1,12 @@
// @flow
import * as electron from 'electron';
import {Readable, Writable} from 'stream';
import {joinUrlAndQueryString} from 'insomnia-url';
import uuid from 'uuid';
import zlib from 'zlib';
import {join as pathJoin} from 'path';
import {format as urlFormat, parse as urlParse} from 'url';
import {DEBOUNCE_MILLIS, getAppVersion, isDevelopment} from './constants';
import * as querystring from './querystring';
const URL_PATH_CHARACTER_WHITELIST = '+,;@=:';
const ESCAPE_REGEX_MATCH = /[-[\]/{}()*+?.\\^$|]/g;
type Header = {
@@ -16,20 +14,6 @@ type Header = {
value: string
};
export function getBasicAuthHeader (username: ?string, password: ?string): Header {
const name = 'Authorization';
const header = `${username || ''}:${password || ''}`;
const authString = Buffer.from(header, 'utf8').toString('base64');
const value = `Basic ${authString}`;
return {name, value};
}
export function getBearerAuthHeader (token: string): Header {
const name = 'Authorization';
const value = `Bearer ${token}`;
return {name, value};
}
export function filterHeaders<T: Header> (headers: Array<T>, name: string): Array<T> {
if (!Array.isArray(headers) || !name) {
return [];
@@ -92,20 +76,6 @@ export function getContentLengthHeader<T: Header> (headers: Array<T>): T | null
return matches.length ? matches[0] : null;
}
export function setDefaultProtocol (url: string, defaultProto: string = 'http:'): string {
// If no url, don't bother returning anything
if (!url) {
return '';
}
// Default the proto if it doesn't exist
if (url.indexOf('://') === -1) {
url = `${defaultProto}//${url}`;
}
return url;
}
/**
* Generate an ID of the format "<MODEL_NAME>_<TIMESTAMP><RANDOM>"
* @param prefix
@@ -121,84 +91,6 @@ export function generateId (prefix: string): string {
}
}
export function flexibleEncodeComponent (str: string, ignore: string = ''): string {
// Sometimes spaces screw things up because of url.parse
str = str.replace(/%20/g, ' ');
// Handle all already-encoded characters so we don't touch them
str = str.replace(/%([0-9a-fA-F]{2})/g, '__ENC__$1');
// Do a special encode of ignored chars, so they aren't touched.
// This first pass, surrounds them with a special tag (anything unique
// will work), so it can change them back later
// Example: will replace %40 with __LEAVE_40_LEAVE__, and we'll change
// it back to %40 at the end.
for (const c of ignore) {
const code = encodeURIComponent(c).replace('%', '');
const re2 = new RegExp(escapeRegex(c), 'g');
str = str.replace(re2, `__RAW__${code}`);
}
// Encode it
str = encodeURIComponent(str);
// Put back the raw version of the ignored chars
for (const match of str.match(/__RAW__([0-9a-fA-F]{2})/g) || []) {
const code = match.replace('__RAW__', '');
str = str.replace(match, decodeURIComponent(`%${code}`));
}
// Put back the encoded version of the ignored chars
for (const match of str.match(/__ENC__([0-9a-fA-F]{2})/g) || []) {
const code = match.replace('__ENC__', '');
str = str.replace(match, `%${code}`);
}
return str;
}
export function prepareUrlForSending (url: string, autoEncode: boolean = true): string {
const urlWithProto = setDefaultProtocol(url);
if (!autoEncode) {
return urlWithProto;
} else {
// Parse the URL into components
const parsedUrl = urlParse(urlWithProto);
// ~~~~~~~~~~~ //
// 1. Pathname //
// ~~~~~~~~~~~ //
if (parsedUrl.pathname) {
const segments = parsedUrl.pathname.split('/');
parsedUrl.pathname = segments.map(
s => flexibleEncodeComponent(s, URL_PATH_CHARACTER_WHITELIST)
).join('/');
}
// ~~~~~~~~~~~~~~ //
// 2. Querystring //
// ~~~~~~~~~~~~~~ //
if (parsedUrl.query) {
const qsParams = querystring.deconstructToParams(parsedUrl.query);
const encodedQsParams = [];
for (const {name, value} of qsParams) {
encodedQsParams.push({
name: flexibleEncodeComponent(name),
value: flexibleEncodeComponent(value)
});
}
parsedUrl.query = querystring.buildFromParams(encodedQsParams);
parsedUrl.search = `?${parsedUrl.query}`;
}
return urlFormat(parsedUrl);
}
}
export function delay (milliseconds: number = DEBOUNCE_MILLIS): Promise<void> {
return new Promise(resolve => setTimeout(resolve, milliseconds));
}
@@ -271,7 +163,7 @@ export function attributeHref (href: string): string {
if (href.match(/^http/i)) {
const appName = isDevelopment() ? 'Insomnia Dev' : 'Insomnia';
const qs = `utm_source=${appName}&utm_medium=app&utm_campaign=v${getAppVersion()}`;
return querystring.joinUrl(href, qs);
return joinUrlAndQueryString(href, qs);
} else {
// Don't modify non-http urls
return href;

View File

@@ -2,9 +2,9 @@
import type {Request} from '../models/request';
import type {BaseModel} from '../models/index';
import {setDefaultProtocol} from 'insomnia-url';
import clone from 'clone';
import * as models from '../models';
import {setDefaultProtocol} from './misc';
import * as db from './database';
import * as templating from '../templating';
import type {CookieJar} from '../models/cookie-jar';

View File

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View File

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 86 KiB

View File

@@ -2,7 +2,7 @@
import electron from 'electron';
import {CHECK_FOR_UPDATES_INTERVAL, getAppVersion, isDevelopment, UPDATE_URL_MAC, UPDATE_URL_WINDOWS} from '../common/constants';
import * as models from '../models/index';
import * as querystring from '../common/querystring';
import {buildQueryStringFromParams, joinUrlAndQueryString} from 'insomnia-url';
import {delay} from '../common/misc';
const {autoUpdater, BrowserWindow, ipcMain} = electron;
@@ -25,8 +25,8 @@ async function getUpdateUrl (force: boolean): Promise<string | null> {
{name: 'channel', value: settings.updateChannel}
];
const qs = querystring.buildFromParams(params);
const fullUrl = querystring.joinUrl(updateUrl, qs);
const qs = buildQueryStringFromParams(params);
const fullUrl = joinUrlAndQueryString(updateUrl, qs);
console.log(`[updater] Using url ${fullUrl}`);
if (isDevelopment()) {

View File

@@ -96,7 +96,7 @@ export function createWindow () {
});
// Load the html of the app.
const appUrl = process.env.APP_RENDER_URL || `file://${__dirname}/renderer.html`;
const appUrl = process.env.APP_RENDER_URL || `file://${app.getAppPath()}/renderer.html`;
console.log(`[main] Loading ${process.env.APP_RENDER_URL}`);
mainWindow.loadURL(appUrl);

Some files were not shown because too many files have changed in this diff Show More