.
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | vMix REST API
2 |
3 |
4 |
5 |
6 | A REST API for vMix Live Video Streaming.
7 |
8 |
9 | ## Features
10 | * [Well-documented API](https://curtgrimes.github.io/vmix-rest-api) for vMix that's built with REST principles in mind.
11 | * **Remote access** to the REST API and vMix's native Web Controller.
12 | * Easily **get inputs, overlays, transitions, and send actions** to a running vMix instance.
13 | * **Standalone [Windows executable](https://github.com/curtgrimes/vmix-rest-api/releases/latest)** that exposes the REST API.
14 | * Runs alongside vMix's existing XML-based API.
15 |
16 | ## Installation
17 | 1. Download the [latest Windows release](https://github.com/curtgrimes/vmix-rest-api/releases/latest)
18 | 1. Run [vMix](https://www.vmix.com) and [enable the Web Controller](https://www.vmix.com/knowledgebase/article.aspx/69/how-to-control-vmix-from-a-web-browser-using-vmix-web-controller).
19 | 1. Open the included [config.yml](config/config.sample.yml) file in a text editor and update it with your Web Controller URL. Optionally enable settings for remote API access and remote Web Controller access.
20 | 1. Run the vMix REST API and you'll be given the local base URL to your REST API (and also remote access URLs if you enabled remote access). Check out the [examples](#examples) to get started or read the [full API documentation](https://curtgrimes.github.io/vmix-rest-api/).
21 |
22 | ## Full API Documentation
23 | Read the [full API documentation](https://curtgrimes.github.io/vmix-rest-api/) to see what the vMix REST API can do.
24 |
25 | ## Examples
26 | In these examples, replace *http://localhost:3000/api/rest/v1* with your own base URL given when you start the vMix REST API.
27 |
28 | ### Get a list of all inputs
29 | **`GET /inputs`**
30 | ```
31 | curl -i -H 'Accept: application/json' http://localhost:3000/api/rest/v1/inputs
32 | ```
33 | Returns:
34 | ```json
35 | [
36 | {
37 | "inputId": 1,
38 | "type": "ImageSequence",
39 | "title": "MVC-001F.JPG",
40 | "state": {
41 | "running": false,
42 | "paused": false,
43 | "completed": true
44 | },
45 | "isActive": false,
46 | "isPreview": true,
47 | "media": {
48 | "position": 0,
49 | "duration": 0,
50 | "loop": false,
51 | "muted": null,
52 | "volume": null,
53 | "balance": null,
54 | "solo": null,
55 | "audiobusses": null,
56 | "audioMeter": {
57 | "left": null,
58 | "right": null
59 | }
60 | },
61 | "list": null,
62 | "fields": []
63 | },
64 | {
65 | "inputId": 2,
66 | "type": "VirtualSet",
67 | "title": "CircularStudio",
68 | "state": {
69 | "running": false,
70 | "paused": true,
71 | "completed": false
72 | },
73 | "isActive": false,
74 | "isPreview": false,
75 | "media": {
76 | "position": 0,
77 | "duration": 0,
78 | "loop": false,
79 | "muted": null,
80 | "volume": null,
81 | "balance": null,
82 | "solo": null,
83 | "audiobusses": null,
84 | "audioMeter": {
85 | "left": null,
86 | "right": null
87 | }
88 | },
89 | "list": null,
90 | "fields": []
91 | }
92 | ]
93 | ```
94 |
95 | ### Get a single input
96 | **`GET /inputs/4`**
97 | ```
98 | curl -i -H 'Accept: application/json' http://localhost:3000/api/rest/v1/inputs/4
99 | ```
100 | Returns:
101 | ```json
102 | {
103 | "inputId": 4,
104 | "type": "Xaml",
105 | "title": "NewsHD.xaml",
106 | "state": {
107 | "running": false,
108 | "paused": true,
109 | "completed": false
110 | },
111 | "isActive": true,
112 | "isPreview": false,
113 | "media": {
114 | "position": 0,
115 | "duration": 0,
116 | "loop": false,
117 | "muted": null,
118 | "volume": null,
119 | "balance": null,
120 | "solo": null,
121 | "audiobusses": null,
122 | "audioMeter": {
123 | "left": null,
124 | "right": null
125 | }
126 | },
127 | "list": null,
128 | "fields": [
129 | {
130 | "fieldId": "Headline",
131 | "type": "text",
132 | "text": "News Brief"
133 | },
134 | {
135 | "fieldId": "Description",
136 | "type": "text",
137 | "text": "January 1, 2020"
138 | }
139 | ]
140 | }
141 | ```
142 |
143 | ### Make input 1 active with a wipe transition effect
144 | **`PUT /inputs/1`**
145 |
146 | Request body
147 |
148 | ```json
149 | {
150 | "isActive": true,
151 | "transitionEffect": "wipe"
152 | }
153 | ```
154 | ```
155 | curl -i -H 'Accept: application/json' -X PUT -d '{"isActive": true,"transitionEffect": "wipe"}' http://localhost:3000/api/rest/v1/inputs/1
156 | ```
157 | Returns 200 on success.
158 |
159 | ### Update the title called "Headline" on input 4
160 | **`PUT /inputs/4/fields/Headline`**
161 |
162 | Request body
163 | ```json
164 | {
165 | "text": "Breaking News"
166 | }
167 | ```
168 | ```
169 | curl -i -H 'Accept: application/json' -X PUT -d '{"text": "Breaking News"}' http://localhost:3000/api/rest/v1/inputs/4/fields/Headline
170 | ```
171 | Returns 200 on success.
172 |
173 | ### Get vMix version information
174 | **`GET /vmix`**
175 | ```
176 | curl -i -H 'Accept: application/json' http://localhost:3000/api/rest/v1/vmix
177 | ```
178 | Returns:
179 | ```json
180 | {
181 | "version": "19.0.0.54",
182 | "edition": "Basic HD"
183 | }
184 | ```
185 |
186 |
187 | ## Development
188 | ### Docker
189 | 1. Build: `docker build . -t vmix-rest-api`
190 | 1. Set environment variable `CONFIG_PATH` to something like `/path/to/config`
191 | 1. Run locally on port 3000: `docker run -it -p 3000:3000 -d vmix-rest-api`
192 | 1. Go to http://localhost:3000/api/rest/v1 and start making requests (like http://localhost:3000/api/rest/v1/inputs)
193 |
194 | ### Nodemon
195 | 1. Run `CONFIG_PATH=/path/to/config.yml nodemon app/index.js`
--------------------------------------------------------------------------------
/app/config.js:
--------------------------------------------------------------------------------
1 | const yamljs = require('yamljs');
2 |
3 | let config;
4 |
5 | const getPath = () => {
6 | return process.cwd() + '/'
7 | + (process.env.CONFIG_PATH
8 | ? process.env.CONFIG_PATH // Use environment variable
9 | : 'config.yml' // Look for it in the current directory
10 | );
11 | };
12 |
13 | const load = () => {
14 | try {
15 | config = yamljs.load(getPath());
16 | return config;
17 | }
18 | catch (error) {
19 | return false;
20 | }
21 | };
22 |
23 | module.exports = {
24 | load,
25 | getPath,
26 | }
--------------------------------------------------------------------------------
/app/index.js:
--------------------------------------------------------------------------------
1 | // Load config first
2 | const config = require('./config').load();
3 |
4 | if (!config) {
5 | // Config not loaded. Splash screen will show
6 | // an error message.
7 | require('./splashScreen')();
8 | return;
9 | }
10 |
11 | var express = require('express');
12 | var app = express();
13 | const routes = require('./routes');
14 | let vmix = require('./services/vmix');
15 | var bodyParser = require('body-parser')
16 | const logger = require('./util/logger');
17 |
18 | if (config.vmix_rest_api.remote_access.enabled) {
19 | require('./remoteAccess').connect();
20 | }
21 |
22 | app.use(bodyParser.json());
23 | app.use(function (req, res, next) {
24 | if (!config.vmix_rest_api.vmix_path) {
25 | res
26 | .status('501')
27 | .send('vmix_Path is not set in config.');
28 | }
29 | else {
30 | next();
31 | }
32 | });
33 |
34 | if (config.vmix_rest_api.logging) {
35 | // Log requests to console
36 | app.use(logger);
37 | }
38 |
39 | app.get('/', async (req, res) => {
40 | let mainPageContent = await require('./mainPage')();
41 | res.send(mainPageContent);
42 | });
43 |
44 | if (config.vmix_rest_api.remote_access.remote_web_controller) {
45 | // Enable proxy to Web Controller
46 | app.get(['/web-controller', '/web-controller*'], vmix.getProxyToWebController());
47 | }
48 |
49 | app.use('/api/rest/v1', routes);
50 |
51 | // Error handling
52 | // Put this last
53 | app.use(function (error, req, res, next) {
54 | console.error(error.message);
55 | res.status(500).send(error.message);
56 | });
57 |
58 | app.listen(config.vmix_rest_api.port);
59 |
60 | require('./splashScreen')();
61 |
--------------------------------------------------------------------------------
/app/mainPage.js:
--------------------------------------------------------------------------------
1 | const configData = require('./config').load();
2 | let remoteAccess = require('./remoteAccess');
3 |
4 | module.exports = async () => {
5 | let remoteConnectionPath = await remoteAccess.connect();
6 | return 'vMix REST API
\
7 | Full API Documentation
\
8 | GitHub
\
9 | \
10 | - vMix REST API base path: http://localhost:' + configData.vmix_rest_api.port +'/api/rest/v1\
11 | \
15 |
\
16 | '+ (remoteConnectionPath ? '\
17 | - Remote access base path: '+ remoteConnectionPath +'\
18 | \
22 |
\
23 | '+ (configData.vmix_rest_api.remote_access.remote_web_controller ? '\
24 | - Web Controller Remote Path: '+ remoteConnectionPath +'/web-controller
\
25 | ' : '')+'\
26 | ' : '')
27 |
28 | +'\
29 |
\
30 | ';
31 | };
--------------------------------------------------------------------------------
/app/remoteAccess.js:
--------------------------------------------------------------------------------
1 | // Before requiring ngrok apply a monkeypatch to fix an issue where
2 | // th ngrok binary can't be called by the system if we're running
3 | // as a packaged app. This is specific to Windows but I haven't tested
4 | // if a similar change would be needed on other platforms (I think it would).
5 | // This changes the behavior of the first spawn() called in ngrok/index.js
6 | (function() {
7 | let path = require('path');
8 | var childProcess = require("child_process");
9 | var oldSpawn = childProcess.spawn;
10 | function mySpawn() {
11 | let command = arguments[0];
12 | let commandArgs = arguments[1];
13 | let settings = arguments[2];
14 |
15 | if (command === './ngrok.exe') {
16 | // Running on Windows platform
17 | command = path.join(process.cwd(), "lib", "ngrok.exe");
18 |
19 | // ngrok sets a cwd that we want to ignore because it will
20 | // be a reference to the /snapshot path that doesn't exist
21 | // to the outside system, and it fails when you give it a
22 | // cwd that doesn't exist
23 | // https://github.com/nodejs/node/issues/11520
24 | settings = {};
25 | }
26 | return oldSpawn.apply(this, [command, commandArgs, settings]);
27 | }
28 | childProcess.spawn = mySpawn;
29 | })();
30 |
31 | var ngrok = require('ngrok');
32 | const config = require('./config').load().vmix_rest_api;
33 |
34 | let remoteConnection;
35 |
36 | let connect = () => {
37 | if (!remoteConnection) {
38 | remoteConnection = new Promise((resolve, reject) => {
39 | ngrok.connect(
40 | config.port,
41 | (error, url) => {
42 | if (error) {
43 | console.log(error);
44 | resolve(false);
45 | }
46 | else if (url) {
47 | resolve(url);
48 | }
49 | }
50 | );
51 | });
52 | }
53 |
54 | return remoteConnection;
55 | };
56 |
57 | let getConnectionData = async () => {
58 | let connection = await connect();
59 | console.log('here');
60 | console.log(connection);
61 | return connection;
62 | }
63 |
64 | module.exports = {
65 | connect,
66 | getConnectionData,
67 | };
--------------------------------------------------------------------------------
/app/routes/index.js:
--------------------------------------------------------------------------------
1 | const routes = require('express').Router();
2 | const inputsRoute = require('./inputs');
3 | const overlaysRoute = require('./overlays');
4 | const transitionsRoute = require('./transitions');
5 | const statusRoute = require('./status');
6 | const vmixRoute = require('./vmix');
7 |
8 | routes.use('/inputs', inputsRoute);
9 | routes.use('/overlays', overlaysRoute);
10 | routes.use('/transitions', transitionsRoute);
11 | routes.use('/status', statusRoute);
12 | routes.use('/vmix', vmixRoute);
13 |
14 | module.exports = routes;
15 |
--------------------------------------------------------------------------------
/app/routes/inputs/active.js:
--------------------------------------------------------------------------------
1 | const inputService = require('../../services/inputs');
2 |
3 | module.exports = async (req, res, next) => {
4 | let all;
5 | try {
6 | all = await inputService.all();
7 | }
8 | catch (error) {
9 | next(error);
10 | return;
11 | }
12 |
13 | const activeInput = all.find((input) => {
14 | return input.isActive;
15 | });
16 |
17 | return activeInput ? res.json(activeInput) : res.sendStatus(204);
18 | };
19 |
--------------------------------------------------------------------------------
/app/routes/inputs/all.js:
--------------------------------------------------------------------------------
1 | const request = require('request');
2 | const inputService = require('../../services/inputs');
3 |
4 | module.exports = async (req, res, next) => {
5 | let all;
6 | try {
7 | all = await inputService.all();
8 | }
9 | catch (error) {
10 | next(error);
11 | return;
12 | }
13 |
14 | return all ? res.json(all) : res.sendStatus(404);
15 | };
16 |
--------------------------------------------------------------------------------
/app/routes/inputs/fields/allForInput.js:
--------------------------------------------------------------------------------
1 | const request = require('request');
2 | const inputService = require('../../../services/inputs');
3 |
4 | module.exports = async (req, res, next) => {
5 | let single;
6 | try {
7 | single = await inputService.byId(req.params.inputId);
8 | }
9 | catch (error) {
10 | next(error);
11 | return;
12 | }
13 |
14 | return single ? res.json(single.fields) : res.sendStatus(404);
15 | };
16 |
--------------------------------------------------------------------------------
/app/routes/inputs/fields/index.js:
--------------------------------------------------------------------------------
1 | const fields = require('express').Router({mergeParams: true});
2 | const allForInput = require('./allForInput');
3 | const singleGet = require('./singleGet');
4 | const singlePut = require('./singlePut');
5 |
6 | fields.get('/', allForInput);
7 | fields.get('/:fieldId', singleGet);
8 | fields.put('/:fieldId', singlePut);
9 |
10 | module.exports = fields;
--------------------------------------------------------------------------------
/app/routes/inputs/fields/singleGet.js:
--------------------------------------------------------------------------------
1 | const inputService = require('../../../services/inputs');
2 |
3 | module.exports = async (req, res, next) => {
4 | let single;
5 | try {
6 | single = await inputService.byId(req.params.inputId);
7 | }
8 | catch (error) {
9 | next(error);
10 | return;
11 | }
12 |
13 | let singleField = single.fields.find((field) => {
14 | return field.fieldId === req.params.fieldId;
15 | });
16 |
17 | return singleField ? res.json(singleField) : res.sendStatus(404);
18 | };
19 |
--------------------------------------------------------------------------------
/app/routes/inputs/fields/singlePut.js:
--------------------------------------------------------------------------------
1 | const inputService = require('../../../services/inputs');
2 | const vmixService = require('../../../services/vmix');
3 |
4 | module.exports = async (req, res, next) => {
5 | let singleInput, singleField;
6 | try {
7 | singleInput = await inputService.byId(req.params.inputId);
8 | }
9 | catch (error) {
10 | next(error);
11 | return;
12 | }
13 |
14 | singleField = singleInput.fields.find((field) => {
15 | return field.fieldId === req.params.fieldId;
16 | });
17 |
18 | if (!singleField) {
19 | res.sendStatus(404);
20 | return;
21 | }
22 |
23 | if (req.body.text && req.body.type === "text") {
24 | if (singleField.text === req.body.text) {
25 | // New text is same as current text;
26 | // nothing to change
27 | res.sendStatus(304); // Not modified
28 | return;
29 | }
30 |
31 | success = await vmixService.execute({
32 | functionName: 'SetText',
33 | inputId: singleInput.inputId,
34 | selectedName: singleField.fieldId,
35 | value: req.body.text,
36 | });
37 | }
38 |
39 | if (req.body.path && req.body.type === "image") {
40 | if (singleField.path === req.body.path) {
41 | // New path is same as current path;
42 | // nothing to change
43 | res.sendStatus(304); // Not modified
44 | return;
45 | }
46 |
47 | success = await vmixService.execute({
48 | functionName: 'SetImage',
49 | inputId: singleInput.inputId,
50 | selectedName: singleField.fieldId,
51 | value: req.body.path,
52 | });
53 | }
54 |
55 | if (success) {
56 | res.sendStatus(200);
57 | }
58 | else {
59 | res.sendStatus(500);
60 | }
61 | };
62 |
--------------------------------------------------------------------------------
/app/routes/inputs/index.js:
--------------------------------------------------------------------------------
1 | const inputs = require('express').Router();
2 | const all = require('./all');
3 | const singleGet = require('./singleGet');
4 | const singlePut = require('./singlePut');
5 | const active = require('./active');
6 | const preview = require('./preview');
7 | const fields = require('./fields');
8 |
9 | inputs.get('/', all);
10 | inputs.use('/:inputId([0-9]+)/fields', fields);
11 | inputs.get('/:inputId([0-9]+)', singleGet);
12 | inputs.put('/:inputId([0-9]+)', singlePut);
13 |
14 | inputs.get('/active', active);
15 | inputs.get('/preview', preview);
16 |
17 | module.exports = inputs;
--------------------------------------------------------------------------------
/app/routes/inputs/preview.js:
--------------------------------------------------------------------------------
1 | const inputService = require('../../services/inputs');
2 |
3 | module.exports = async (req, res, next) => {
4 | let all;
5 | try {
6 | all = await inputService.all();
7 | }
8 | catch (error) {
9 | next(error);
10 | return;
11 | }
12 | const activeInput = all.find((input) => {
13 | return input.isPreview;
14 | });
15 |
16 | return activeInput ? res.json(activeInput) : res.sendStatus(204);
17 | };
18 |
--------------------------------------------------------------------------------
/app/routes/inputs/singleGet.js:
--------------------------------------------------------------------------------
1 | const inputService = require('../../services/inputs');
2 |
3 | module.exports = async (req, res, next) => {
4 | let single;
5 | try {
6 | single = await inputService.byId(req.params.inputId);
7 | }
8 | catch (error) {
9 | next(error);
10 | return;
11 | }
12 |
13 | return single ? res.json(single) : res.sendStatus(404);
14 | };
15 |
--------------------------------------------------------------------------------
/app/routes/inputs/singlePut.js:
--------------------------------------------------------------------------------
1 | const stringToBoolean = require('../../util/stringToBoolean');
2 | const inputService = require('../../services/inputs');
3 | const vmixService = require('../../services/vmix');
4 | const transitionEffects = require('../../services/transitions').effects;
5 |
6 | module.exports = async (req, res, next) => {
7 | let single;
8 | try {
9 | single = await inputService.byId(req.params.inputId);
10 | }
11 | catch (error) {
12 | next(error);
13 | return;
14 | }
15 |
16 | let success, transitionEffect;
17 |
18 | if (!single) {
19 | res.sendStatus(404);
20 | return;
21 | }
22 |
23 | if (req.body.transitionEffect) {
24 | let transitionEffectIndex = transitionEffects.map((effect) => { return effect.toLowerCase();})
25 | .indexOf(req.body.transitionEffect.toLowerCase());
26 | if (transitionEffectIndex >= 0) {
27 | // Valid transition value also provided
28 | transitionEffect = transitionEffects[transitionEffectIndex];
29 | }
30 | }
31 |
32 | if (typeof(req.body.isActive) === 'boolean' && req.body.isActive !== single.isActive) {
33 | // Make active input preview and make preview active
34 | if (req.body.isActive === true) {
35 | // Cut to this input
36 | success = await vmixService.execute({
37 | functionName: transitionEffect ? transitionEffect : 'Cut',
38 | inputId: single.inputId,
39 | });
40 | }
41 | else {
42 | // Cut away from this input (to the preview -- I couldn't find
43 | // a simple way to just blank the active view without a fade
44 | // and without making a new input).
45 | success = await vmixService.execute({
46 | functionName: transitionEffect ? transitionEffect : 'Cut',
47 | inputId: inputService.PREVIEW_INPUT_ID,
48 | });
49 | }
50 |
51 | if (success) {
52 | res.sendStatus(200);
53 | }
54 | else {
55 | res.sendStatus(500);
56 | }
57 | }
58 | else {
59 | // No changes to make
60 | res.sendStatus(200);
61 | }
62 | };
63 |
--------------------------------------------------------------------------------
/app/routes/overlays/all.js:
--------------------------------------------------------------------------------
1 | const overlayService = require('../../services/overlays');
2 |
3 | module.exports = async (req, res, next) => {
4 | let all;
5 | try {
6 | all = await overlayService.all();
7 | }
8 | catch (error) {
9 | next(error);
10 | return;
11 | }
12 |
13 | return all ? res.json(all) : res.sendStatus(404);
14 | };
15 |
--------------------------------------------------------------------------------
/app/routes/overlays/index.js:
--------------------------------------------------------------------------------
1 | const overlays = require('express').Router();
2 | const all = require('./all');
3 | const single = require('./single');
4 |
5 | overlays.get('/', all);
6 | overlays.get('/:overlayId', single);
7 |
8 | module.exports = overlays;
--------------------------------------------------------------------------------
/app/routes/overlays/single.js:
--------------------------------------------------------------------------------
1 |
2 | const overlayService = require('../../services/overlays');
3 |
4 | module.exports = async (req, res, next) => {
5 | let overlays;
6 |
7 | try {
8 | overlays = await overlayService.all();
9 | }
10 | catch (error) {
11 | next(error);
12 | return;
13 | }
14 |
15 | let single = overlays.find((overlay) => {
16 | return overlay.overlayId === parseInt(req.params.overlayId);
17 | });
18 |
19 | return single ? res.json(single) : res.sendStatus(404);
20 | };
21 |
--------------------------------------------------------------------------------
/app/routes/status/index.js:
--------------------------------------------------------------------------------
1 | const status = require('express').Router();
2 | const vmixService = require('../../services/vmix').getData;
3 | const lodashGet = require('lodash/get');
4 | const boolean = require('boolean');
5 |
6 | status.get('/', async (req, res, next) => {
7 | let vmixData;
8 |
9 | try {
10 | vmixData = await vmixService();
11 | }
12 | catch (error) {
13 | next(error);
14 | return;
15 | }
16 |
17 | res.json({
18 | fadeToBlack: boolean(lodashGet(vmixData, 'version[0]._text[0]')),
19 | recording: boolean(lodashGet(vmixData, 'recording[0]._text[0]')),
20 | external: boolean(lodashGet(vmixData, 'external[0]._text[0]')),
21 | streaming: boolean(lodashGet(vmixData, 'streaming[0]._text[0]')),
22 | playlistActive: boolean(lodashGet(vmixData, 'playList[0]._text[0]')),
23 | multiCorder: boolean(lodashGet(vmixData, 'multiCorder[0]._text[0]')),
24 | fullscreen: boolean(lodashGet(vmixData, 'fullscreen[0]._text[0]')),
25 | });
26 | });
27 |
28 | module.exports = status;
--------------------------------------------------------------------------------
/app/routes/transitions/all.js:
--------------------------------------------------------------------------------
1 | const transitionService = require('../../services/transitions');
2 |
3 | module.exports = async (req, res, next) => {
4 | let all;
5 |
6 | try {
7 | all = await transitionService.all();
8 | }
9 | catch (error) {
10 | next(error);
11 | return;
12 | }
13 |
14 | return all ? res.json(all) : res.sendStatus(404);
15 | };
16 |
--------------------------------------------------------------------------------
/app/routes/transitions/index.js:
--------------------------------------------------------------------------------
1 | const transitions = require('express').Router();
2 | const all = require('./all');
3 | const single = require('./single');
4 |
5 | transitions.get('/', all);
6 | transitions.get('/:transitionId', single);
7 |
8 | module.exports = transitions;
--------------------------------------------------------------------------------
/app/routes/transitions/single.js:
--------------------------------------------------------------------------------
1 |
2 | const transitionService = require('../../services/transitions');
3 |
4 | module.exports = async (req, res) => {
5 | let transitions;
6 | try {
7 | transitions = await transitionService.all();
8 | }
9 | catch (error) {
10 | next(error);
11 | return;
12 | }
13 |
14 | let single = transitions.find((transition) => {
15 | return transition.transitionId === parseInt(req.params.transitionId);
16 | });
17 |
18 | return single ? res.json(single) : res.sendStatus(404);
19 | };
20 |
--------------------------------------------------------------------------------
/app/routes/vmix/index.js:
--------------------------------------------------------------------------------
1 | const vmix = require('express').Router();
2 | const vmixService = require('../../services/vmix').getData;
3 | const lodashGet = require('lodash/get');
4 | const boolean = require('boolean');
5 |
6 | vmix.get('/', async (req, res) => {
7 | let vmixData;
8 | try {
9 | vmixData = await vmixService();
10 | }
11 | catch (error) {
12 | next(error);
13 | return;
14 | }
15 |
16 | res.json({
17 | version: lodashGet(vmixData, 'version[0]._text[0]'),
18 | edition: lodashGet(vmixData, 'edition[0]._text[0]'),
19 | });
20 | });
21 |
22 | module.exports = vmix;
--------------------------------------------------------------------------------
/app/services/fields.js:
--------------------------------------------------------------------------------
1 | const lodashGet = require('lodash/get');
2 |
3 | const getFields = (input) => {
4 | let textFields = lodashGet(input, 'text', [])
5 | .map((field) => {
6 | return {
7 | fieldId: lodashGet(field, '_attributes.name', null),
8 | type: 'text',
9 | text: lodashGet(field, '_text[0]', null),
10 | };
11 | });
12 | let imageFields = lodashGet(input, 'image', [])
13 | .map((field) => {
14 | return {
15 | fieldId: lodashGet(field, '_attributes.name', null),
16 | type: 'image',
17 | path: lodashGet(field, '_text[0]', null),
18 | };
19 | });
20 | return [...textFields, ...imageFields];
21 | };
22 |
23 | module.exports = {
24 | getFields
25 | };
--------------------------------------------------------------------------------
/app/services/inputs.js:
--------------------------------------------------------------------------------
1 | const vmixService = require('./vmix');
2 | const fieldService = require('./fields');
3 | const lodashGet = require('lodash/get');
4 | const boolean = require('boolean');
5 |
6 | let all = async () => {
7 | const vmixData = await vmixService.getData();
8 | const inputs = lodashGet(vmixData, 'inputs[0].input', []);
9 |
10 | const activeInputId = parseInt(lodashGet(vmixData, 'active[0]._text[0]'));
11 | const previewInputId = parseInt(lodashGet(vmixData, 'preview[0]._text[0]'));
12 |
13 | return inputs.map((input) => {
14 | const inputId = parseInt(lodashGet(input, '_attributes.number', null));
15 |
16 | // List
17 | let list = lodashGet(input, 'list[0].item', []).map((listItem) => {
18 | return {
19 | path: lodashGet(listItem, '_text[0]', null),
20 | selected: boolean(lodashGet(listItem, '_attributes.selected', null)),
21 | };
22 | });
23 |
24 | let meterL = lodashGet(input, '_attributes.meterF1', null);
25 | let meterR = lodashGet(input, '_attributes.meterF2', null);
26 |
27 | return {
28 | inputId: inputId,
29 | type: lodashGet(input, '_attributes.type', null),
30 | title: lodashGet(input, '_attributes.title', null),
31 | state: {
32 | running: lodashGet(input, '_attributes.state', null) === 'Running',
33 | paused: lodashGet(input, '_attributes.state', null) === 'Paused',
34 | completed: lodashGet(input, '_attributes.state', null) === 'Completed',
35 | },
36 | isActive: activeInputId === inputId,
37 | isPreview: previewInputId === inputId,
38 |
39 | media: {
40 | position: parseFloat(lodashGet(input, '_attributes.position', null)),
41 | duration: parseFloat(lodashGet(input, '_attributes.duration', null)),
42 | loop: boolean(lodashGet(input, '_attributes.loop', null)),
43 | muted: (lodashGet(input, '_attributes.muted', null) ? boolean(lodashGet(input, '_attributes.muted', null)) : null),
44 | volume: (lodashGet(input, '_attributes.volume', null) ? parseInt(lodashGet(input, '_attributes.volume', null)) : null),
45 | balance: (lodashGet(input, '_attributes.balance', null) ? parseInt(lodashGet(input, '_attributes.balance', null)) : null),
46 | solo: (lodashGet(input, '_attributes.solo', null) ? boolean(lodashGet(input, '_attributes.solo', null)) : null),
47 | audiobusses: (lodashGet(input, '_attributes.audiobusses', null) ? lodashGet(input, '_attributes.audiobusses', null) : null),
48 | audioMeter: {
49 | left: parseFloat(meterL),
50 | right: parseFloat(meterR),
51 | },
52 | },
53 |
54 | // List
55 | list: (list.length ? list : null),
56 |
57 | // Put last
58 | fields: fieldService.getFields(input),
59 | };
60 |
61 | // Make 'loop' value a boolean if it's present
62 | if (lodashGet(input, '_attributes.loop')) {
63 | input._attributes.loop = (input._attributes.loop.toLowerCase() === 'true');
64 | }
65 |
66 | return input['_attributes'];
67 | });
68 | };
69 |
70 | let byId = async (inputId) => {
71 | let inputs = await all();
72 | return inputs.find((input) => {
73 | return input.inputId === parseInt(inputId);
74 | });
75 | };
76 |
77 | let makeActive = (inputId) => {
78 | return vmixService.exec('CutDirect', inputId);
79 | };
80 |
81 | module.exports = {
82 | all: all,
83 | byId: byId,
84 | makeActive: makeActive,
85 | PREVIEW_INPUT_ID: 0,
86 | ACTIVE_INPUT_ID: -1,
87 | };
--------------------------------------------------------------------------------
/app/services/overlays.js:
--------------------------------------------------------------------------------
1 | const vmixService = require('./vmix').getData;
2 | const lodashGet = require('lodash/get');
3 | const inputService = require('./inputs');
4 |
5 | let all = async () => {
6 | let vmix = await vmixService();
7 | let overlays = lodashGet(vmix, 'overlays[0].overlay', []);
8 | return Promise.all(overlays.map(async (overlay) => {
9 | let input = await inputService.byId(
10 | lodashGet(overlay, '_text[0]', null)
11 | );
12 |
13 | return {
14 | overlayId: parseInt(lodashGet(overlay, '_attributes.number', null)),
15 | inputId: input ? input.inputId : null,
16 | };
17 | }));
18 | };
19 |
20 | let byId = async (overlayId) => {
21 | let overlays = await all();
22 | return overlays.find((overlay) => {
23 | return overlay.overlayId === parseInt(overlayId);
24 | });
25 | };
26 |
27 | module.exports = {
28 | all: all,
29 | byId: byId,
30 | };
--------------------------------------------------------------------------------
/app/services/transitions.js:
--------------------------------------------------------------------------------
1 | const vmixService = require('./vmix').getData;
2 | const lodashGet = require('lodash/get');
3 |
4 | let all = async () => {
5 | let vmix = await vmixService();
6 | let transitions = lodashGet(vmix, 'transitions[0].transition', []);
7 |
8 | return transitions.map((transition) => {
9 | return {
10 | transitionId: parseInt(lodashGet(transition, '_attributes.number', null)),
11 | effect: lodashGet(transition, '_attributes.effect', null),
12 | duration: parseInt(lodashGet(transition, '_attributes.duration', null)),
13 | };
14 | });
15 | };
16 |
17 | let byId = async (transitionId) => {
18 | let transitions = await all();
19 | return transitions.find((transition) => {
20 | return transition.transitionId === transitionId;
21 | });
22 | };
23 |
24 | let effects = [
25 | 'Fade',
26 | 'Zoom',
27 | 'Wipe',
28 | 'Slide',
29 | 'Fly',
30 | 'CrossZoom',
31 | 'FlyRotate',
32 | 'Cube',
33 | 'CubeZoom',
34 | 'VerticalWipe',
35 | 'VerticalSlide',
36 | 'Merge',
37 | 'WipeReverse',
38 | 'SlideReverse',
39 | 'VerticalWipeReverse',
40 | 'VerticalSlideReverse',
41 | 'Cut',
42 | ];
43 |
44 | module.exports = {
45 | all: all,
46 | byId: byId,
47 | effects: effects,
48 | };
--------------------------------------------------------------------------------
/app/services/vmix.js:
--------------------------------------------------------------------------------
1 | const request = require('request-promise');
2 | const xml2js = require('xml-js').xml2js;
3 | const lodashGet = require('lodash/get');
4 | const config = require('../config').load();
5 | var proxy = require('express-http-proxy');
6 |
7 | const vmixLoadTimeout = 4000;
8 |
9 | const getVmixPath = () => {
10 | return config.vmix_rest_api.vmix_path + (!stringEndsWithAPIPath(config.vmix_rest_api.vmix_path) ? '/api' : '');
11 | };
12 |
13 | function stringEndsWithAPIPath(string) {
14 | let matches = string.match(".*\/api$");
15 | return matches !== null && matches.length > 0;
16 | }
17 |
18 | const getData = () => {
19 | return new Promise((resolve, reject) => {
20 | request(getVmixPath(), {timeout: vmixLoadTimeout}, (error, response, xml) => {
21 | if (error || !xml) {
22 | return;
23 | }
24 |
25 | let vmixAPIObject = xml2js(xml, {
26 | compact: true,
27 | alwaysArray: true,
28 | });
29 |
30 | resolve(lodashGet(vmixAPIObject, 'vmix[0]'));
31 | })
32 | .catch((error) => {
33 | reject('Unable to connect to vMix at ' + getVmixPath());
34 | });
35 | })
36 | .catch((error) => {
37 | throw new Error(error);
38 | });
39 | };
40 |
41 | const execute = ({functionName, inputId, selectedName, value}) => {
42 | // Execute what vMix calls a function
43 | const path = getVmixPath() +
44 | '?Function=' + encodeURIComponent(functionName) +
45 | (inputId ? '&Input=' + encodeURIComponent(inputId) : '') +
46 | (value ? '&Value=' + encodeURIComponent(value) : '') +
47 | (selectedName ? '&SelectedName=' + encodeURIComponent(selectedName) : '');
48 |
49 | return new Promise((resolve) => {
50 | request(path, function (error, response, xml) {
51 | if (response.statusCode === 200) {
52 | resolve(true);
53 | }
54 | else {
55 | resolve(false);
56 | }
57 | })
58 | });
59 | };
60 |
61 | let connected = () => {
62 | return new Promise(async (resolve, reject) => {
63 | try {
64 | let vmixData = await getData();
65 | resolve(true);
66 | }
67 | catch (error) {
68 | resolve(false);
69 | }
70 | })
71 | };
72 |
73 | let getProxyToWebController = () => {
74 | return proxy(getVmixPath().replace('/api',''), {
75 | proxyReqPathResolver: function(req) {
76 | return req.url.replace('/web-controller','');
77 | },
78 | userResDecorator: function(proxyRes, proxyResData, userReq, userRes) {
79 | if (proxyRes.headers['content-type'].includes('image')) {
80 | // Don't mess with the incoming image; pass it through as is
81 | return proxyResData;
82 | }
83 | else {
84 | // Replace some paths to things
85 | return proxyResData.toString('utf8')
86 | .replace(/href="\//g,'href="/web-controller/')
87 | .replace(/src="\//g,'src="/web-controller/')
88 | .replace(/url\('\//g, 'url(\'/web-controller/')
89 | .replace(/\/controllerupdate/g, '/web-controller/controllerupdate')
90 | .replace(/\/api/g, '/web-controller/api')
91 | .replace(/\/tallyupdate/g, '/web-controller/tallyupdate');
92 | }
93 | }
94 | })
95 | };
96 |
97 | module.exports = {
98 | getData,
99 | execute,
100 | connected,
101 | vmixLoadTimeout,
102 | getProxyToWebController,
103 | }
--------------------------------------------------------------------------------
/app/splashScreen.js:
--------------------------------------------------------------------------------
1 | const config = require('./config');
2 | const figlet = require('figlet');
3 | const pad = require('pad');
4 | const vmix = require('./services/vmix');
5 | let remoteAccess = require('./remoteAccess');
6 | // const font = require('figlet/fonts/Standard.flf');
7 | // const version = require('./util/packageVersion').version;
8 | var table = require('table').table;
9 | var wordwrap = require('wordwrap');
10 | var center = require('center-align');
11 | const chalk = require('chalk');
12 |
13 | module.exports = async () => {
14 |
15 | console.log(chalk.yellowBright.bold(" \
16 | __ __ _ ____ _____ ____ _____ _ ____ ___\n\
17 | __ _| \\/ (_)_ __ | _ \\| ____/ ___|_ _| / \\ | _ \\_ _|\n\
18 | \\ \\ / / |\\/| | \\ \\/ / | |_) | _| \\___ \\ | | / _ \\ | |_) | |\n\
19 | \\ V /| | | | |> < | _ <| |___ ___) || | / ___ \\| __/| |\n\
20 | \\_/ |_| |_|_/_/\\_\\ |_| \\_\\_____|____/ |_| /_/ \\_\\_| |___|\n\
21 | "));
22 |
23 | // console.log(chalk.blueBright(figlet.textSync('vMix REST API')));
24 | // console.log(center('Version '+version + '\n', 65));
25 | console.log(center('Version 0.4.0\n', 63));
26 | console.log(center('Visit '+ chalk.magentaBright('https://curtgrimes.github.io/vmix-rest-api/') + ' for documentation.\n', 75));
27 |
28 | let connectedToVmix,
29 | vmixConnectionRetryInterval,
30 | remoteConnectionPath;
31 |
32 | const configData = config.load();
33 |
34 | if (!configData) {
35 | console.log(wordwrap(0,70)(chalk.red.bold('Looking for config file and could not find it at '+config.getPath()+'. Make sure that config.yml is present in the same folder as this file.\n')));
36 | return;
37 | }
38 |
39 | if (!(await vmix.connected())) {
40 | console.log(wordwrap(0,70)(chalk.red.bold('Unable to connect to vMix at '+ configData.vmix_rest_api.vmix_path +'. Make sure vMix is running, Web Controller is on, and you\'ve set the correct Web Controller URL in the config file located at '+ config.getPath() +'.\n\nRestart the vMix REST API to try again.')));
41 | return;
42 | }
43 |
44 | if (configData.vmix_rest_api.remote_access.enabled) {
45 | remoteConnectionPath = await remoteAccess.connect();
46 | }
47 |
48 | console.log(center('Open '+ chalk.greenBright('http://localhost:' + configData.vmix_rest_api.port) + ' in a browser to begin.\n', 75));
49 |
50 | console.log(table([
51 | [chalk.bold('vMix REST API'), 'http://localhost:'+ configData.vmix_rest_api.port],
52 | ...(remoteConnectionPath ? [[chalk.bold('Remote Access'), remoteConnectionPath]] : []),
53 | ...(remoteConnectionPath && configData.vmix_rest_api.remote_access.remote_web_controller ? [[chalk.bold('Web Controller Remote Access'), remoteConnectionPath + '/web-controller']] : []),
54 |
55 | [chalk.bold('Local Web Controller'), configData.vmix_rest_api.vmix_path],
56 | [chalk.bold('Config'), config.getPath()],
57 | ], {
58 | columns: {
59 | 0: {
60 | width: 25,
61 | },
62 | 1: {
63 | width: 33,
64 | },
65 | }
66 | }));
67 | };
--------------------------------------------------------------------------------
/app/util/logger.js:
--------------------------------------------------------------------------------
1 | module.exports = require('morgan')('dev');
2 |
--------------------------------------------------------------------------------
/app/util/packageVersion.js:
--------------------------------------------------------------------------------
1 | require('pkginfo')(module, 'version');
--------------------------------------------------------------------------------
/app/util/stringToBoolean.js:
--------------------------------------------------------------------------------
1 | module.exports = (input) => {
2 | if (!(typeof input === 'string')) {
3 | return;
4 | }
5 |
6 | if (input.toLowerCase() === 'true') {
7 | return true;
8 | }
9 | else if (input.toLowerCase() === 'false') {
10 | return false;
11 | }
12 | else {
13 | return;
14 | }
15 | };
--------------------------------------------------------------------------------
/config/config.sample.yml:
--------------------------------------------------------------------------------
1 | vmix_rest_api:
2 |
3 | # Path provided by vMix under Settings > Web Controller
4 | # If vMix is running on the current host, you should be
5 | # able to use localhost instead of an IP address.
6 | # Examples: http://10.211.55.6:8080, http://localhost:8080
7 | vmix_path: http://localhost:8088
8 |
9 | # Port to make the API available on localhost. Don't use
10 | # the same port as vMix above.
11 | # Example: 3000
12 | port: 3000
13 |
14 | # Log requests to the console
15 | logging: false
16 |
17 | # Remote access
18 | # Allow remote access to the REST API outside of your
19 | # local network. You'll be given an access URL when
20 | # you start the REST API. This URL changes every time
21 | # you start the REST API.
22 | remote_access:
23 | enabled: false
24 |
25 | # Make the native vMix Web Controller available remotely.
26 | # You'll be given the remote web controller URL when you
27 | # start the REST API.
28 | remote_web_controller: false
29 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | # Dir for API portal deploy
2 | web_deploy
3 |
4 | # Logs
5 | logs
6 | *.log
7 | npm-debug.log*
8 |
9 | # Runtime data
10 | pids
11 | *.pid
12 | *.seed
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # node-waf configuration
21 | .lock-wscript
22 |
23 | # Compiled binary addons (http://nodejs.org/api/addons.html)
24 | build/Release
25 |
26 | # Dependency directory
27 | node_modules
28 |
29 | # Optional npm cache directory
30 | .npm
31 |
32 | # Optional REPL history
33 | .node_repl_history
34 |
--------------------------------------------------------------------------------
/docs/.yo-rc.json:
--------------------------------------------------------------------------------
1 | {
2 | "generator-openapi-repo": {
3 | "importExistingSpec": true,
4 | "importedSpec": "swagger.yaml",
5 | "name": "vMix REST",
6 | "repo": "curtgrimes/vmix-rest-api",
7 | "splitSpec": false,
8 | "samples": true,
9 | "installSwaggerUI": true
10 | }
11 | }
--------------------------------------------------------------------------------
/docs/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Ivan Goncharov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # vMix REST OpenAPI Specification
2 | [](https://travis-ci.org/curtgrimes/vmix-rest-api)
3 |
4 | ## Steps to finish
5 |
6 | 1. Enable [Travis](https://docs.travis-ci.com/user/getting-started/#To-get-started-with-Travis-CI%3A) for your repository (**note**: you already have `.travis.yml` file)
7 | 2. [Create GitHub access token](https://help.github.com/articles/creating-an-access-token-for-command-line-use/); check `public_repo` on `Select scopes` section.
8 | 3. Use the token value as a value for [Travis environment variable](https://docs.travis-ci.com/user/environment-variables/#Defining-Variables-in-Repository-Settings) with the name `GH_TOKEN`
9 | 4. Make a test commit to trigger CI: `git commit --allow-empty -m "Test Travis CI" && git push`
10 | 5. Wait until Travis build is finished. You can check progress by clicking on the `Build Status` badge at the top
11 | 6. If you did everything correct, https://curtgrimes.github.io/vmix-rest-api/ will lead to your new docs
12 | 7. **[Optional]** You can setup [custom domain](https://help.github.com/articles/using-a-custom-domain-with-github-pages/) (just create `web/CNAME` file)
13 | 8. Start writing/editing your OpenAPI spec: check out [usage](#usage) section below
14 | 9. **[Optional]** If you document public API consider adding it into [APIs.guru](https://APIs.guru) directory using [this form](https://apis.guru/add-api/).
15 | 10. Delete this section :smile:
16 |
17 | ## Links
18 |
19 | - Documentation(ReDoc): https://curtgrimes.github.io/vmix-rest-api/
20 | - SwaggerUI: https://curtgrimes.github.io/vmix-rest-api/swagger-ui/
21 | - Look full spec:
22 | + JSON https://curtgrimes.github.io/vmix-rest-api/swagger.json
23 | + YAML https://curtgrimes.github.io/vmix-rest-api/swagger.yaml
24 | - Preview spec version for branch `[branch]`: https://curtgrimes.github.io/vmix-rest-api/preview/[branch]
25 |
26 | **Warning:** All above links are updated only after Travis CI finishes deployment
27 |
28 | ## Working on specification
29 | ### Install
30 |
31 | 1. Install [Node JS](https://nodejs.org/)
32 | 2. Clone repo and `cd`
33 | + Run `npm install`
34 |
35 | ### Usage
36 |
37 | 1. Run `npm start`
38 | 2. Checkout console output to see where local server is started. You can use all [links](#links) (except `preview`) by replacing https://curtgrimes.github.io/vmix-rest-api/ with url from the message: `Server started `
39 | 3. Make changes using your favorite editor or `swagger-editor` (look for URL in console output)
40 | 4. All changes are immediately propagated to your local server, moreover all documentation pages will be automagically refreshed in a browser after each change
41 | **TIP:** you can open `swagger-editor`, documentation and `swagger-ui` in parallel
42 | 5. Once you finish with the changes you can run tests using: `npm test`
43 | 6. Share you changes with the rest of the world by pushing to GitHub :smile:
44 |
--------------------------------------------------------------------------------
/docs/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var util = require('gulp-util')
3 | var gulpConnect = require('gulp-connect');
4 | var connect = require('connect');
5 | var cors = require('cors');
6 | var path = require('path');
7 | var exec = require('child_process').exec;
8 | var portfinder = require('portfinder');
9 | var swaggerRepo = require('swagger-repo');
10 |
11 | var DIST_DIR = 'web_deploy';
12 |
13 | gulp.task('serve', ['build', 'watch', 'edit'], function() {
14 | portfinder.getPort({port: 3000}, function (err, port) {
15 | gulpConnect.server({
16 | root: [DIST_DIR],
17 | livereload: true,
18 | port: port,
19 | middleware: function (gulpConnect, opt) {
20 | return [
21 | cors()
22 | ]
23 | }
24 | });
25 | });
26 | });
27 |
28 | gulp.task('edit', function() {
29 | portfinder.getPort({port: 5000}, function (err, port) {
30 | var app = connect();
31 | app.use(swaggerRepo.swaggerEditorMiddleware());
32 | app.listen(port);
33 | util.log(util.colors.green('swagger-editor started http://localhost:' + port));
34 | });
35 | });
36 |
37 | gulp.task('build', function (cb) {
38 | exec('npm run build', function (err, stdout, stderr) {
39 | console.log(stderr);
40 | cb(err);
41 | });
42 | });
43 |
44 | gulp.task('reload', ['build'], function () {
45 | gulp.src(DIST_DIR).pipe(gulpConnect.reload())
46 | });
47 |
48 | gulp.task('watch', function () {
49 | gulp.watch(['spec/**/*', 'web/**/*'], ['reload']);
50 | });
51 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vMix-REST-openapi-spec",
3 | "version": "0.0.1",
4 | "dependencies": {
5 | "bower": "^1.7.7",
6 | "connect": "^3.4.1",
7 | "cors": "^2.7.1",
8 | "deploy-to-gh-pages": "^1.1.0",
9 | "gulp": "^3.9.1",
10 | "gulp-connect": "^4.2.0",
11 | "gulp-util": "^3.0.7",
12 | "portfinder": "^1.0.3",
13 | "shelljs": "^0.7.0",
14 | "swagger-repo": "^1.0.0",
15 | "swagger-ui": "^2.1.4"
16 | },
17 | "private": true,
18 | "scripts": {
19 | "deploy": "npm run build && deploy-to-gh-pages --update web_deploy",
20 | "build": "node ./scripts/build.js",
21 | "swagger": "swagger-repo",
22 | "test": "swagger-repo validate",
23 | "start": "gulp serve",
24 | "deploy-branch": "node ./scripts/deploy-branch.js"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/docs/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/curtgrimes/vmix-rest-api/b495cfe2d81670f4eae113da363b39b3ef84808e/docs/screenshot.png
--------------------------------------------------------------------------------
/docs/scripts/build.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | 'use strict';
3 | var Path = require('path');
4 |
5 | require('shelljs/global');
6 | set('-e');
7 |
8 | mkdir('-p', 'web_deploy')
9 |
10 | cp('-R', 'web/*', 'web_deploy/');
11 |
12 | exec('npm run swagger bundle -- -o web_deploy/swagger.json');
13 | exec('npm run swagger bundle -- --yaml -o web_deploy/swagger.yaml');
14 |
15 | var SWAGGER_UI_DIST = Path.dirname(require.resolve('swagger-ui'));
16 | rm('-rf', 'web_deploy/swagger-ui/')
17 | cp('-R', SWAGGER_UI_DIST, 'web_deploy/swagger-ui/')
18 | sed('-i', 'http://petstore.swagger.io/v2/swagger.json', '../swagger.json', 'web_deploy/swagger-ui/index.html')
19 |
20 |
--------------------------------------------------------------------------------
/docs/scripts/deploy-branch.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | 'use strict';
3 | require('shelljs/global');
4 | var path = require('path');
5 |
6 | set('-e');
7 | set('-v');
8 |
9 | var branch = process.env.TRAVIS_BRANCH && process.env.TRAVIS_BRANCH.toLowerCase();
10 | if (branch && branch !== 'gh-pages') {
11 | var branchPath = path.join('.tmp', 'preview', branch, '/');
12 | mkdir('-p', branchPath);
13 | exec('npm run swagger bundle -- -o ' + branchPath + 'swagger.json');
14 | cp('web/index.html', branchPath);
15 | exec('deploy-to-gh-pages --update .tmp');
16 | }
17 |
--------------------------------------------------------------------------------
/docs/spec/README.md:
--------------------------------------------------------------------------------
1 | ## Global headers
2 |
3 | In order to minimize duplications you can use `headers` global object (similar to `definitions`, `responses`).
4 | During build process all references to global `headers` will be inlined and `headers` will be removed form resulting spec so spec will be valid (global `headers` is not allowed by Swagger spec):
5 |
6 | Example:
7 | ```yaml
8 | ...
9 | headers:
10 | Rate-Limit-Limit:
11 | description: The number of allowed requests in the current period
12 | type: integer
13 | ...
14 | paths:
15 | /api-keys:
16 | get:
17 | summary: Retrieve a list of api keys
18 | responses:
19 | 200:
20 | description: A list of api keys was retrieved successfully
21 | headers:
22 | Rate-Limit-Limit:
23 | $ref: "#/headers/Rate-Limit-Limit"
24 | ```
25 |
--------------------------------------------------------------------------------
/docs/spec/code_samples/README.md:
--------------------------------------------------------------------------------
1 | Code samples
2 | =====
3 |
4 | Generate [x-code-samples](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md#x-code-samples)
5 | Path `//.` where:
6 | * `` - name of the language from [this](https://github.com/github/linguist/blob/master/lib/linguist/popular.yml) list.
7 | * `` - path of target method, where all slashes replaced with `@` sign.
8 | * `` - verb of target method.
9 | * `` - ignored.
10 |
--------------------------------------------------------------------------------
/docs/spec/swagger.yaml:
--------------------------------------------------------------------------------
1 | swagger: "2.0"
2 | info:
3 | version: "0.4"
4 | title: "vMix REST API"
5 | host: "localhost:3000"
6 | basePath: "/api/rest/v1"
7 | tags:
8 | - name: "input"
9 | description: "Different sources that can be loaded into vMix"
10 | - name: "fields"
11 | description: "Titles and images on an input"
12 | - name: "overlay"
13 | description: "A container for an input that can be displayed on top of other inputs"
14 | - name: "transition"
15 | description: "A preset for changing from one input to another"
16 | - name: "vmix"
17 | description: "Version information about vMix"
18 | - name: "status"
19 | description: "High-level state information about the vMix environment"
20 | schemes:
21 | - "http"
22 | paths:
23 | /inputs:
24 | get:
25 | tags:
26 | - "input"
27 | summary: "Retrieve a list of inputs"
28 | produces:
29 | - "application/json"
30 | responses:
31 | 200:
32 | description: "A list of inputs was retrieved successfully"
33 | schema:
34 | type: "array"
35 | items:
36 | $ref: "#/definitions/Input"
37 | examples:
38 | application/json:
39 | [
40 | {
41 | "inputId": 1,
42 | "type": "ImageSequence",
43 | "title": "MVC-001F.JPG",
44 | "state": {
45 | "running": false,
46 | "paused": false,
47 | "completed": true
48 | },
49 | "isActive": false,
50 | "isPreview": true,
51 | "media": {
52 | "position": 0,
53 | "duration": 0,
54 | "loop": false,
55 | "muted": null,
56 | "volume": null,
57 | "balance": null,
58 | "solo": null,
59 | "audiobusses": null,
60 | "audioMeter": {
61 | "left": null,
62 | "right": null
63 | }
64 | },
65 | "list": null,
66 | "fields": []
67 | },
68 | {
69 | "inputId": 2,
70 | "type": "VirtualSet",
71 | "title": "CircularStudio",
72 | "state": {
73 | "running": false,
74 | "paused": true,
75 | "completed": false
76 | },
77 | "isActive": false,
78 | "isPreview": false,
79 | "media": {
80 | "position": 0,
81 | "duration": 0,
82 | "loop": false,
83 | "muted": null,
84 | "volume": null,
85 | "balance": null,
86 | "solo": null,
87 | "audiobusses": null,
88 | "audioMeter": {
89 | "left": null,
90 | "right": null
91 | }
92 | },
93 | "list": null,
94 | "fields": []
95 | }
96 | ]
97 | 501:
98 | description: "Unable to connect to vMix"
99 | /inputs/{inputId}:
100 | get:
101 | tags:
102 | - "input"
103 | summary: "Retrieve a specific input"
104 | produces:
105 | - "application/json"
106 | parameters:
107 | - name: "inputId"
108 | in: "path"
109 | description: "ID of input to return"
110 | required: true
111 | type: "integer"
112 | responses:
113 | 200:
114 | description: "A specific input was retrieved successfully"
115 | schema:
116 | $ref: "#/definitions/Input"
117 | examples:
118 | application/json:
119 | {
120 | "inputId": 4,
121 | "type": "Xaml",
122 | "title": "NewsHD.xaml",
123 | "state": {
124 | "running": false,
125 | "paused": true,
126 | "completed": false
127 | },
128 | "isActive": true,
129 | "isPreview": false,
130 | "media": {
131 | "position": 0,
132 | "duration": 0,
133 | "loop": false,
134 | "muted": null,
135 | "volume": null,
136 | "balance": null,
137 | "solo": null,
138 | "audiobusses": null,
139 | "audioMeter": {
140 | "left": null,
141 | "right": null
142 | }
143 | },
144 | "list": null,
145 | "fields": [
146 | {
147 | "fieldId": "Headline",
148 | "type": "text",
149 | "text": "News Brief"
150 | },
151 | {
152 | "fieldId": "Description",
153 | "type": "text",
154 | "text": "January 1, 2020"
155 | }
156 | ]
157 | }
158 | 501:
159 | description: "Unable to connect to vMix"
160 | put:
161 | tags:
162 | - "input"
163 | summary: "Update a specific input"
164 | produces:
165 | - "application/json"
166 | parameters:
167 | - name: "inputId"
168 | in: "path"
169 | description: "ID of input to update"
170 | required: true
171 | type: "integer"
172 | - name: "input"
173 | in: "body"
174 | description: "The updates to make to this input."
175 | schema:
176 | type: object
177 | properties:
178 | isActive:
179 | type: boolean
180 | description: "Use `true` to set this input as active; use `false` to put this input in the preview and make the input currently in the preview active."
181 | transitionEffect:
182 | type: string
183 | description: "Optional transition effect. Only applies if `isActive` is set and causing the currently active input to change visibility. Use one of the following values: `Fade`, `Zoom`, `Wipe`, `Slide`, `Fly`, `CrossZoom`, `FlyRotate`, `Cube`, `CubeZoom`, `VerticalWipe`, `VerticalSlide`, `Merge`, `WipeReverse`, `SlideReverse`, `VerticalWipeReverse`, `VerticalSlideReverse`, `Cut`."
184 | responses:
185 | 200:
186 | description: "A specific input was updated successfully"
187 | schema:
188 | $ref: "#/definitions/Input"
189 | examples:
190 | application/json:
191 | {
192 | "inputId": 4,
193 | "type": "Xaml",
194 | "title": "NewsHD.xaml",
195 | "state": {
196 | "running": false,
197 | "paused": true,
198 | "completed": false
199 | },
200 | "isActive": true,
201 | "isPreview": false,
202 | "media": {
203 | "position": 0,
204 | "duration": 0,
205 | "loop": false,
206 | "muted": null,
207 | "volume": null,
208 | "balance": null,
209 | "solo": null,
210 | "audiobusses": null,
211 | "audioMeter": {
212 | "left": null,
213 | "right": null
214 | }
215 | },
216 | "list": null,
217 | "fields": [
218 | {
219 | "fieldId": "Headline",
220 | "type": "text",
221 | "text": "News Brief"
222 | },
223 | {
224 | "fieldId": "Description",
225 | "type": "text",
226 | "text": "January 1, 2020"
227 | }
228 | ]
229 | }
230 | 501:
231 | description: "Unable to connect to vMix"
232 | /inputs/active:
233 | get:
234 | tags:
235 | - "input"
236 | summary: "Retrieve the active input"
237 | produces:
238 | - "application/json"
239 | responses:
240 | 200:
241 | description: "The input was retrieved successfully"
242 | schema:
243 | $ref: "#/definitions/Input"
244 | examples:
245 | application/json:
246 | {
247 | "inputId": 4,
248 | "type": "Xaml",
249 | "title": "NewsHD.xaml",
250 | "state": {
251 | "running": false,
252 | "paused": true,
253 | "completed": false
254 | },
255 | "isActive": true,
256 | "isPreview": false,
257 | "media": {
258 | "position": 0,
259 | "duration": 0,
260 | "loop": false,
261 | "muted": null,
262 | "volume": null,
263 | "balance": null,
264 | "solo": null,
265 | "audiobusses": null,
266 | "audioMeter": {
267 | "left": null,
268 | "right": null
269 | }
270 | },
271 | "list": null,
272 | "fields": [
273 | {
274 | "fieldId": "Headline",
275 | "type": "text",
276 | "text": "News Brief"
277 | },
278 | {
279 | "fieldId": "Description",
280 | "type": "text",
281 | "text": "January 1, 2020"
282 | }
283 | ]
284 | }
285 | 204:
286 | description: "No active input found"
287 | 501:
288 | description: "Unable to connect to vMix"
289 | /inputs/preview:
290 | get:
291 | tags:
292 | - "input"
293 | summary: "Retrieve the input currently in preview"
294 | produces:
295 | - "application/json"
296 | responses:
297 | 200:
298 | description: "The input was retrieved successfully"
299 | schema:
300 | $ref: "#/definitions/Input"
301 | examples:
302 | application/json:
303 | {
304 | "inputId": 4,
305 | "type": "Xaml",
306 | "title": "NewsHD.xaml",
307 | "state": {
308 | "running": false,
309 | "paused": true,
310 | "completed": false
311 | },
312 | "isActive": false,
313 | "isPreview": true,
314 | "media": {
315 | "position": 0,
316 | "duration": 0,
317 | "loop": false,
318 | "muted": null,
319 | "volume": null,
320 | "balance": null,
321 | "solo": null,
322 | "audiobusses": null,
323 | "audioMeter": {
324 | "left": null,
325 | "right": null
326 | }
327 | },
328 | "list": null,
329 | "fields": [
330 | {
331 | "fieldId": "Headline",
332 | "type": "text",
333 | "text": "News Brief"
334 | },
335 | {
336 | "fieldId": "Description",
337 | "type": "text",
338 | "text": "January 1, 2020"
339 | }
340 | ]
341 | }
342 | 204:
343 | description: "No preview input found"
344 | 501:
345 | description: "Unable to connect to vMix"
346 | /input/{inputId}/fields:
347 | get:
348 | tags:
349 | - "fields"
350 | summary: "Get a list of titles and images that belong to an input"
351 | produces:
352 | - "application/json"
353 | parameters:
354 | - name: "inputId"
355 | in: "path"
356 | description: "ID of input to get"
357 | required: true
358 | type: "integer"
359 | responses:
360 | 200:
361 | description: "A list of fields was retrieved successfully"
362 | schema:
363 | type: "array"
364 | items:
365 | $ref: "#/definitions/Field"
366 | examples:
367 | application/json:
368 | [
369 | {
370 | "fieldId": "Headline",
371 | "type": "text",
372 | "text": "News Brief"
373 | },
374 | {
375 | "fieldId": "Description",
376 | "type": "text",
377 | "text": "January 1, 2020"
378 | }
379 | ]
380 | 501:
381 | description: "Unable to connect to vMix"
382 | /input/{inputId}/fields/{fieldId}:
383 | get:
384 | tags:
385 | - "fields"
386 | summary: "Get a specific title or image that belongs to an input"
387 | produces:
388 | - "application/json"
389 | parameters:
390 | - name: "inputId"
391 | in: "path"
392 | description: "ID of input"
393 | required: true
394 | type: "integer"
395 | - name: "fieldId"
396 | in: "path"
397 | description: "ID of field"
398 | required: true
399 | type: "integer"
400 | responses:
401 | 200:
402 | description: "A specific field was retrieved successfully"
403 | schema:
404 | $ref: "#/definitions/Field"
405 | examples:
406 | application/json:
407 | {
408 | "fieldId": "Headline",
409 | "type": "text",
410 | "text": "News Brief"
411 | }
412 | 501:
413 | description: "Unable to connect to vMix"
414 | put:
415 | tags:
416 | - "fields"
417 | summary: "Update a specific title or image that belongs to an input"
418 | consumes:
419 | - "application/json"
420 | produces:
421 | - "application/json"
422 | parameters:
423 | - name: "inputId"
424 | in: "path"
425 | description: "ID of input"
426 | required: true
427 | type: "integer"
428 | - name: "fieldId"
429 | in: "path"
430 | description: "ID of field"
431 | required: true
432 | type: "integer"
433 | - name: "field"
434 | in: "body"
435 | description: "The updates to make to this field. `text` only applies if `type=\"text\"`. `path` only applies if `type=\"image\"`. Include either `text` or `path`, but not both."
436 | schema:
437 | type: object
438 | properties:
439 | text:
440 | type: string
441 | description: "Only valid if type = \"text\""
442 | path:
443 | type: string
444 | description: "Only valid if type = \"image\""
445 | responses:
446 | 200:
447 | description: "A specific field was updated successfully"
448 | 501:
449 | description: "Unable to connect to vMix"
450 | /overlays:
451 | get:
452 | tags:
453 | - "overlay"
454 | summary: "Retrieve a list of overlays"
455 | produces:
456 | - "application/json"
457 | responses:
458 | 200:
459 | description: "A list of overlays was retrieved successfully"
460 | schema:
461 | type: "array"
462 | items:
463 | $ref: "#/definitions/Overlay"
464 | examples:
465 | application/json:
466 | [
467 | {
468 | "overlayId": 1,
469 | "inputId": null
470 | },
471 | {
472 | "overlayId": 2,
473 | "inputId": 1
474 | },
475 | {
476 | "overlayId": 3,
477 | "inputId": null
478 | },
479 | {
480 | "overlayId": 4,
481 | "inputId": null
482 | }
483 | ]
484 | 501:
485 | description: "Unable to connect to vMix"
486 | /overlays/{overlayId}:
487 | get:
488 | tags:
489 | - "overlay"
490 | summary: "Retrieve a specific overlay"
491 | produces:
492 | - "application/json"
493 | parameters:
494 | - name: "overlayId"
495 | in: "path"
496 | description: "ID of overlay to return"
497 | required: true
498 | type: "integer"
499 | responses:
500 | 200:
501 | description: "A specific overlay was retrieved successfully"
502 | schema:
503 | $ref: "#/definitions/Overlay"
504 | examples:
505 | application/json:
506 | {
507 | "overlayId": 2,
508 | "inputId": 1
509 | }
510 | 501:
511 | description: "Unable to connect to vMix"
512 | /transitions:
513 | get:
514 | tags:
515 | - "transition"
516 | summary: "Retrieve a list of transitions"
517 | produces:
518 | - "application/json"
519 | responses:
520 | 200:
521 | description: "A list of transitions was retrieved successfully"
522 | schema:
523 | type: "array"
524 | items:
525 | $ref: "#/definitions/Transition"
526 | examples:
527 | application/json:
528 | [
529 | {
530 | "transitionId": 1,
531 | "effect": "Fade",
532 | "duration": 500
533 | },
534 | {
535 | "transitionId": 2,
536 | "effect": "Merge",
537 | "duration": 1000
538 | }
539 | ]
540 | 501:
541 | description: "Unable to connect to vMix"
542 | /transitions/{transitionId}:
543 | get:
544 | tags:
545 | - "transition"
546 | summary: "Retrieve a specific transition"
547 | produces:
548 | - "application/json"
549 | parameters:
550 | - name: "transitionId"
551 | in: "path"
552 | description: "ID of transition to return"
553 | required: true
554 | type: "integer"
555 | responses:
556 | 200:
557 | description: "A specific transition was retrieved successfully"
558 | schema:
559 | $ref: "#/definitions/Transition"
560 | examples:
561 | application/json:
562 | {
563 | "transitionId": 1,
564 | "effect": "Fade",
565 | "duration": 500
566 | }
567 | 501:
568 | description: "Unable to connect to vMix"
569 | /vmix:
570 | get:
571 | tags:
572 | - "vmix"
573 | summary: "Get vMix version information"
574 | produces:
575 | - "application/json"
576 | responses:
577 | 200:
578 | description: "Version information was retrieved successfully"
579 | schema:
580 | $ref: "#/definitions/vmix"
581 | examples:
582 | application/json:
583 | {
584 | "version": "19.0.0.54",
585 | "edition": "Basic HD"
586 | }
587 | 501:
588 | description: "vMix is not running"
589 | /status:
590 | get:
591 | tags:
592 | - "status"
593 | summary: "Get high-level state information about the vMix environment"
594 | produces:
595 | - "application/json"
596 | responses:
597 | 200:
598 | description: "State information was retrieved successfully"
599 | schema:
600 | $ref: "#/definitions/status"
601 | examples:
602 | application/json:
603 | {
604 | "fadeToBlack": false,
605 | "recording": false,
606 | "external": false,
607 | "streaming": false,
608 | "playlistActive": false,
609 | "multiCorder": false,
610 | "fullscreen": false
611 | }
612 | 501:
613 | description: "vMix is not running"
614 | definitions:
615 | vmix:
616 | type: "object"
617 | properties:
618 | version:
619 | type: "string"
620 | edition:
621 | type: "string"
622 | status:
623 | type: "object"
624 | properties:
625 | fadeToBlack:
626 | type: "boolean"
627 | recording:
628 | type: "boolean"
629 | external:
630 | type: "boolean"
631 | streaming:
632 | type: "boolean"
633 | playlistActive:
634 | type: "boolean"
635 | multiCorder:
636 | type: "boolean"
637 | fullscreen:
638 | type: "boolean"
639 | Input:
640 | type: "object"
641 | properties:
642 | inputId:
643 | type: "integer"
644 | type:
645 | type: "string"
646 | title:
647 | type: "string"
648 | state:
649 | type: "object"
650 | properties:
651 | running:
652 | type: "boolean"
653 | paused:
654 | type: "boolean"
655 | completed:
656 | type: "boolean"
657 | isActive:
658 | type: "boolean"
659 | default: false
660 | isPreview:
661 | type: "boolean"
662 | default: false
663 | media:
664 | type: "object"
665 | properties:
666 | position:
667 | type: "integer"
668 | duration:
669 | type: "integer"
670 | loop:
671 | type: "boolean"
672 | default: false
673 | muted:
674 | type: "boolean"
675 | volume:
676 | type: "integer"
677 | balance:
678 | type: "integer"
679 | solo:
680 | type: "boolean"
681 | audiobusses:
682 | type: "string"
683 | audiometer:
684 | type: "object"
685 | properties:
686 | left:
687 | type: "integer"
688 | right:
689 | type: "integer"
690 | list:
691 | type: "array"
692 | description: "If `type=\"VideoList\"`, this contains the items in that VideoList."
693 | items:
694 | type: "object"
695 | properties:
696 | path:
697 | type: "string"
698 | selected:
699 | type: "boolean"
700 | fields:
701 | type: "array"
702 | items:
703 | $ref: "#/definitions/Field"
704 | Field:
705 | type: "object"
706 | properties:
707 | fieldId:
708 | type: "string"
709 | type:
710 | type: "string"
711 | text:
712 | type: "string"
713 | description: "Only present if type = \"text\""
714 | path:
715 | type: "string"
716 | description: "Only present if type = \"image\""
717 | Overlay:
718 | type: "object"
719 | properties:
720 | overlayId:
721 | type: "integer"
722 | inputId:
723 | type: "integer"
724 | Transition:
725 | type: "object"
726 | properties:
727 | transitionId:
728 | type: "integer"
729 | effect:
730 | type: "string"
731 | duration:
732 | type: "integer"
733 | xml:
734 | name: "Input"
--------------------------------------------------------------------------------
/docs/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | vMix REST API Specification
5 |
6 |
7 |
8 |
12 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vmix-rest-api",
3 | "version": "0.4.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@types/node": {
8 | "version": "8.9.4",
9 | "resolved": "https://registry.npmjs.org/@types/node/-/node-8.9.4.tgz",
10 | "integrity": "sha512-dSvD36qnQs78G1BPsrZFdPpvLgMW/dnvr5+nTW2csMs5TiP9MOXrjUbnMZOEwnIuBklXtn7b6TPA2Cuq07bDHA=="
11 | },
12 | "abbrev": {
13 | "version": "1.1.1",
14 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
15 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
16 | },
17 | "accepts": {
18 | "version": "1.3.4",
19 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz",
20 | "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=",
21 | "requires": {
22 | "mime-types": "2.1.17",
23 | "negotiator": "0.6.1"
24 | }
25 | },
26 | "ajv": {
27 | "version": "5.5.2",
28 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
29 | "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
30 | "requires": {
31 | "co": "4.6.0",
32 | "fast-deep-equal": "1.0.0",
33 | "fast-json-stable-stringify": "2.0.0",
34 | "json-schema-traverse": "0.3.1"
35 | }
36 | },
37 | "ajv-keywords": {
38 | "version": "2.1.1",
39 | "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz",
40 | "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I="
41 | },
42 | "align-text": {
43 | "version": "1.0.2",
44 | "resolved": "https://registry.npmjs.org/align-text/-/align-text-1.0.2.tgz",
45 | "integrity": "sha512-uBPDs72zrRTdiTBY0YjBbuBOdXtRyT4qsKPb4bL4O7vH4utz/7KjwTJVsVbdThxMbVzkRGAfk8Ml3xoMvXSEYw==",
46 | "requires": {
47 | "kind-of": "5.1.0",
48 | "longest": "2.0.1",
49 | "repeat-string": "1.6.1"
50 | }
51 | },
52 | "ansi-regex": {
53 | "version": "2.1.1",
54 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
55 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
56 | },
57 | "ansi-styles": {
58 | "version": "3.2.0",
59 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
60 | "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
61 | "requires": {
62 | "color-convert": "1.9.1"
63 | }
64 | },
65 | "argparse": {
66 | "version": "1.0.9",
67 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
68 | "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=",
69 | "requires": {
70 | "sprintf-js": "1.0.3"
71 | }
72 | },
73 | "array-filter": {
74 | "version": "0.0.1",
75 | "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz",
76 | "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw="
77 | },
78 | "array-flatten": {
79 | "version": "1.1.1",
80 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
81 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
82 | },
83 | "array-map": {
84 | "version": "0.0.0",
85 | "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz",
86 | "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI="
87 | },
88 | "array-reduce": {
89 | "version": "0.0.0",
90 | "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz",
91 | "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys="
92 | },
93 | "ascii-table": {
94 | "version": "0.0.9",
95 | "resolved": "https://registry.npmjs.org/ascii-table/-/ascii-table-0.0.9.tgz",
96 | "integrity": "sha1-BqZgTWpV1L9BqaR9mHLXp42jHnM="
97 | },
98 | "asn1": {
99 | "version": "0.2.3",
100 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
101 | "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y="
102 | },
103 | "assert-plus": {
104 | "version": "1.0.0",
105 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
106 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
107 | },
108 | "async": {
109 | "version": "0.9.2",
110 | "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz",
111 | "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0="
112 | },
113 | "asynckit": {
114 | "version": "0.4.0",
115 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
116 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
117 | },
118 | "aws-sign2": {
119 | "version": "0.7.0",
120 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
121 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
122 | },
123 | "aws4": {
124 | "version": "1.6.0",
125 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz",
126 | "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4="
127 | },
128 | "balanced-match": {
129 | "version": "1.0.0",
130 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
131 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
132 | },
133 | "basic-auth": {
134 | "version": "2.0.0",
135 | "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.0.tgz",
136 | "integrity": "sha1-AV2z81PgLlY3d1X5YnQuiYHnu7o=",
137 | "requires": {
138 | "safe-buffer": "5.1.1"
139 | }
140 | },
141 | "bcrypt-pbkdf": {
142 | "version": "1.0.1",
143 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
144 | "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=",
145 | "optional": true,
146 | "requires": {
147 | "tweetnacl": "0.14.5"
148 | }
149 | },
150 | "binary": {
151 | "version": "0.3.0",
152 | "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz",
153 | "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=",
154 | "requires": {
155 | "buffers": "0.1.1",
156 | "chainsaw": "0.1.0"
157 | }
158 | },
159 | "bl": {
160 | "version": "0.9.5",
161 | "resolved": "https://registry.npmjs.org/bl/-/bl-0.9.5.tgz",
162 | "integrity": "sha1-wGt5evCF6gC8Unr8jvzxHeIjIFQ=",
163 | "requires": {
164 | "readable-stream": "1.0.34"
165 | }
166 | },
167 | "bluebird": {
168 | "version": "3.5.1",
169 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
170 | "integrity": "sha1-2VUfnemPH82h5oPRfukaBgLuLrk="
171 | },
172 | "body-parser": {
173 | "version": "1.18.2",
174 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz",
175 | "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=",
176 | "requires": {
177 | "bytes": "3.0.0",
178 | "content-type": "1.0.4",
179 | "debug": "2.6.9",
180 | "depd": "1.1.2",
181 | "http-errors": "1.6.2",
182 | "iconv-lite": "0.4.19",
183 | "on-finished": "2.3.0",
184 | "qs": "6.5.1",
185 | "raw-body": "2.3.2",
186 | "type-is": "1.6.15"
187 | }
188 | },
189 | "boolean": {
190 | "version": "0.1.3",
191 | "resolved": "https://registry.npmjs.org/boolean/-/boolean-0.1.3.tgz",
192 | "integrity": "sha1-J0SznniCr6uA3Kjc9RW99OBS5QA="
193 | },
194 | "boom": {
195 | "version": "4.3.1",
196 | "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz",
197 | "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=",
198 | "requires": {
199 | "hoek": "4.2.0"
200 | }
201 | },
202 | "brace-expansion": {
203 | "version": "1.1.11",
204 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
205 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
206 | "requires": {
207 | "balanced-match": "1.0.0",
208 | "concat-map": "0.0.1"
209 | }
210 | },
211 | "buffers": {
212 | "version": "0.1.1",
213 | "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz",
214 | "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s="
215 | },
216 | "bytes": {
217 | "version": "3.0.0",
218 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
219 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
220 | },
221 | "caseless": {
222 | "version": "0.12.0",
223 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
224 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
225 | },
226 | "center-align": {
227 | "version": "1.0.1",
228 | "resolved": "https://registry.npmjs.org/center-align/-/center-align-1.0.1.tgz",
229 | "integrity": "sha1-Kh/f22zQ3S0l+/z2dR50azE+UKQ=",
230 | "requires": {
231 | "align-text": "1.0.2",
232 | "repeat-string": "1.6.1"
233 | }
234 | },
235 | "chainsaw": {
236 | "version": "0.1.0",
237 | "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz",
238 | "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=",
239 | "requires": {
240 | "traverse": "0.3.9"
241 | }
242 | },
243 | "chalk": {
244 | "version": "2.3.1",
245 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz",
246 | "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==",
247 | "requires": {
248 | "ansi-styles": "3.2.0",
249 | "escape-string-regexp": "1.0.5",
250 | "supports-color": "5.2.0"
251 | }
252 | },
253 | "clear": {
254 | "version": "0.0.1",
255 | "resolved": "https://registry.npmjs.org/clear/-/clear-0.0.1.tgz",
256 | "integrity": "sha1-5RhuIp2ZRIF5wTAxG2+dML/2sLo="
257 | },
258 | "clone": {
259 | "version": "1.0.3",
260 | "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.3.tgz",
261 | "integrity": "sha1-KY1+IjFmD0DAA8LtMUDezz9TCF8="
262 | },
263 | "co": {
264 | "version": "4.6.0",
265 | "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
266 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
267 | },
268 | "color-convert": {
269 | "version": "1.9.1",
270 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
271 | "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
272 | "requires": {
273 | "color-name": "1.1.3"
274 | }
275 | },
276 | "color-name": {
277 | "version": "1.1.3",
278 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
279 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
280 | },
281 | "colors": {
282 | "version": "1.0.3",
283 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
284 | "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs="
285 | },
286 | "combined-stream": {
287 | "version": "1.0.5",
288 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz",
289 | "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=",
290 | "requires": {
291 | "delayed-stream": "1.0.0"
292 | }
293 | },
294 | "commander": {
295 | "version": "2.14.1",
296 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.14.1.tgz",
297 | "integrity": "sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw=="
298 | },
299 | "concat-map": {
300 | "version": "0.0.1",
301 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
302 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
303 | },
304 | "content-disposition": {
305 | "version": "0.5.2",
306 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
307 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
308 | },
309 | "content-type": {
310 | "version": "1.0.4",
311 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
312 | "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js="
313 | },
314 | "cookie": {
315 | "version": "0.3.1",
316 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
317 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
318 | },
319 | "cookie-signature": {
320 | "version": "1.0.6",
321 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
322 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
323 | },
324 | "core-util-is": {
325 | "version": "1.0.2",
326 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
327 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
328 | },
329 | "cross-spawn": {
330 | "version": "6.0.4",
331 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.4.tgz",
332 | "integrity": "sha512-LDYnK41m8td+nBTk5Jmn55aGVP18iYuUqoM1X3u+ptt7M/g9FPS8C38PNoJTMfjoNx4fmiwWToPpiZklGRLbIA==",
333 | "requires": {
334 | "nice-try": "1.0.4",
335 | "path-key": "2.0.1",
336 | "semver": "5.5.0",
337 | "shebang-command": "1.2.0",
338 | "which": "1.3.0"
339 | },
340 | "dependencies": {
341 | "semver": {
342 | "version": "5.5.0",
343 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
344 | "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
345 | }
346 | }
347 | },
348 | "cryptiles": {
349 | "version": "3.1.2",
350 | "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz",
351 | "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=",
352 | "requires": {
353 | "boom": "5.2.0"
354 | },
355 | "dependencies": {
356 | "boom": {
357 | "version": "5.2.0",
358 | "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz",
359 | "integrity": "sha1-XdnabuOl8wIHdDYpDLcX0/SlTgI=",
360 | "requires": {
361 | "hoek": "4.2.0"
362 | }
363 | }
364 | }
365 | },
366 | "ctype": {
367 | "version": "0.5.3",
368 | "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz",
369 | "integrity": "sha1-gsGMJGH3QRTvFsE1IkrQuRRMoS8="
370 | },
371 | "cycle": {
372 | "version": "1.0.3",
373 | "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz",
374 | "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI="
375 | },
376 | "dashdash": {
377 | "version": "1.14.1",
378 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
379 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
380 | "requires": {
381 | "assert-plus": "1.0.0"
382 | }
383 | },
384 | "debug": {
385 | "version": "2.6.9",
386 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
387 | "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=",
388 | "requires": {
389 | "ms": "2.0.0"
390 | }
391 | },
392 | "decompress-zip": {
393 | "version": "0.3.0",
394 | "resolved": "https://registry.npmjs.org/decompress-zip/-/decompress-zip-0.3.0.tgz",
395 | "integrity": "sha1-rjvLfjTGWHmt/nfhnDD4ZgK0vbA=",
396 | "requires": {
397 | "binary": "0.3.0",
398 | "graceful-fs": "4.1.11",
399 | "mkpath": "0.1.0",
400 | "nopt": "3.0.6",
401 | "q": "1.2.0",
402 | "readable-stream": "1.1.14",
403 | "touch": "0.0.3"
404 | },
405 | "dependencies": {
406 | "readable-stream": {
407 | "version": "1.1.14",
408 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
409 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
410 | "requires": {
411 | "core-util-is": "1.0.2",
412 | "inherits": "2.0.3",
413 | "isarray": "0.0.1",
414 | "string_decoder": "0.10.31"
415 | }
416 | }
417 | }
418 | },
419 | "defaults": {
420 | "version": "1.0.3",
421 | "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz",
422 | "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=",
423 | "requires": {
424 | "clone": "1.0.3"
425 | }
426 | },
427 | "delayed-stream": {
428 | "version": "1.0.0",
429 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
430 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
431 | },
432 | "depd": {
433 | "version": "1.1.2",
434 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
435 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
436 | },
437 | "destroy": {
438 | "version": "1.0.4",
439 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
440 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
441 | },
442 | "ecc-jsbn": {
443 | "version": "0.1.1",
444 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
445 | "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
446 | "optional": true,
447 | "requires": {
448 | "jsbn": "0.1.1"
449 | }
450 | },
451 | "ee-first": {
452 | "version": "1.1.1",
453 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
454 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
455 | },
456 | "encodeurl": {
457 | "version": "1.0.2",
458 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
459 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
460 | },
461 | "es6-promise": {
462 | "version": "4.2.4",
463 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz",
464 | "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ=="
465 | },
466 | "escape-html": {
467 | "version": "1.0.3",
468 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
469 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
470 | },
471 | "escape-string-regexp": {
472 | "version": "1.0.5",
473 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
474 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
475 | },
476 | "etag": {
477 | "version": "1.8.1",
478 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
479 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
480 | },
481 | "execspawn": {
482 | "version": "1.0.1",
483 | "resolved": "https://registry.npmjs.org/execspawn/-/execspawn-1.0.1.tgz",
484 | "integrity": "sha1-gob53efOzeeQX73ATiTzaPI/jaY=",
485 | "requires": {
486 | "util-extend": "1.0.3"
487 | }
488 | },
489 | "express": {
490 | "version": "4.16.2",
491 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz",
492 | "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=",
493 | "requires": {
494 | "accepts": "1.3.4",
495 | "array-flatten": "1.1.1",
496 | "body-parser": "1.18.2",
497 | "content-disposition": "0.5.2",
498 | "content-type": "1.0.4",
499 | "cookie": "0.3.1",
500 | "cookie-signature": "1.0.6",
501 | "debug": "2.6.9",
502 | "depd": "1.1.2",
503 | "encodeurl": "1.0.2",
504 | "escape-html": "1.0.3",
505 | "etag": "1.8.1",
506 | "finalhandler": "1.1.0",
507 | "fresh": "0.5.2",
508 | "merge-descriptors": "1.0.1",
509 | "methods": "1.1.2",
510 | "on-finished": "2.3.0",
511 | "parseurl": "1.3.2",
512 | "path-to-regexp": "0.1.7",
513 | "proxy-addr": "2.0.2",
514 | "qs": "6.5.1",
515 | "range-parser": "1.2.0",
516 | "safe-buffer": "5.1.1",
517 | "send": "0.16.1",
518 | "serve-static": "1.13.1",
519 | "setprototypeof": "1.1.0",
520 | "statuses": "1.3.1",
521 | "type-is": "1.6.15",
522 | "utils-merge": "1.0.1",
523 | "vary": "1.1.2"
524 | }
525 | },
526 | "express-http-proxy": {
527 | "version": "1.1.0",
528 | "resolved": "https://registry.npmjs.org/express-http-proxy/-/express-http-proxy-1.1.0.tgz",
529 | "integrity": "sha1-tTOEKt0VtwIdeNJIyAtTV4p7a6A=",
530 | "requires": {
531 | "debug": "3.1.0",
532 | "es6-promise": "4.2.4",
533 | "raw-body": "2.3.2"
534 | },
535 | "dependencies": {
536 | "debug": {
537 | "version": "3.1.0",
538 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
539 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
540 | "requires": {
541 | "ms": "2.0.0"
542 | }
543 | }
544 | }
545 | },
546 | "extend": {
547 | "version": "3.0.1",
548 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
549 | "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ="
550 | },
551 | "extsprintf": {
552 | "version": "1.3.0",
553 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
554 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
555 | },
556 | "eyes": {
557 | "version": "0.1.8",
558 | "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
559 | "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A="
560 | },
561 | "fast-deep-equal": {
562 | "version": "1.0.0",
563 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz",
564 | "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8="
565 | },
566 | "fast-json-stable-stringify": {
567 | "version": "2.0.0",
568 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
569 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
570 | },
571 | "figlet": {
572 | "version": "1.2.0",
573 | "resolved": "https://registry.npmjs.org/figlet/-/figlet-1.2.0.tgz",
574 | "integrity": "sha1-bEZTc3j6tkkUa1phQ92gGbQwtBA="
575 | },
576 | "finalhandler": {
577 | "version": "1.1.0",
578 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz",
579 | "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=",
580 | "requires": {
581 | "debug": "2.6.9",
582 | "encodeurl": "1.0.2",
583 | "escape-html": "1.0.3",
584 | "on-finished": "2.3.0",
585 | "parseurl": "1.3.2",
586 | "statuses": "1.3.1",
587 | "unpipe": "1.0.0"
588 | }
589 | },
590 | "forever-agent": {
591 | "version": "0.6.1",
592 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
593 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
594 | },
595 | "form-data": {
596 | "version": "2.3.1",
597 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz",
598 | "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=",
599 | "requires": {
600 | "asynckit": "0.4.0",
601 | "combined-stream": "1.0.5",
602 | "mime-types": "2.1.17"
603 | }
604 | },
605 | "forwarded": {
606 | "version": "0.1.2",
607 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
608 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
609 | },
610 | "fresh": {
611 | "version": "0.5.2",
612 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
613 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
614 | },
615 | "fs.realpath": {
616 | "version": "1.0.0",
617 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
618 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
619 | },
620 | "generate-function": {
621 | "version": "2.0.0",
622 | "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz",
623 | "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ="
624 | },
625 | "generate-object-property": {
626 | "version": "1.2.0",
627 | "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz",
628 | "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=",
629 | "requires": {
630 | "is-property": "1.0.2"
631 | }
632 | },
633 | "get-version": {
634 | "version": "1.1.3",
635 | "resolved": "https://registry.npmjs.org/get-version/-/get-version-1.1.3.tgz",
636 | "integrity": "sha1-UzU2ftcNIQeptKuedecspgPA3tI=",
637 | "requires": {
638 | "lodash": "3.6.0",
639 | "minimist": "1.1.1",
640 | "q": "1.2.0",
641 | "request": "2.54.0",
642 | "require-dir": "0.3.0",
643 | "semver": "4.3.1",
644 | "winston": "0.9.0"
645 | },
646 | "dependencies": {
647 | "ansi-styles": {
648 | "version": "2.2.1",
649 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
650 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
651 | },
652 | "asn1": {
653 | "version": "0.1.11",
654 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz",
655 | "integrity": "sha1-VZvhg3bQik7E2+gId9J4GGObLfc="
656 | },
657 | "assert-plus": {
658 | "version": "0.1.5",
659 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz",
660 | "integrity": "sha1-7nQAlBMALYTOxyGcasgRgS5yMWA="
661 | },
662 | "aws-sign2": {
663 | "version": "0.5.0",
664 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz",
665 | "integrity": "sha1-xXED96F/wDfwLXwuZLYC6iI/fWM="
666 | },
667 | "bluebird": {
668 | "version": "2.11.0",
669 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz",
670 | "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE="
671 | },
672 | "boom": {
673 | "version": "2.10.1",
674 | "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz",
675 | "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=",
676 | "requires": {
677 | "hoek": "2.16.3"
678 | }
679 | },
680 | "caseless": {
681 | "version": "0.9.0",
682 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.9.0.tgz",
683 | "integrity": "sha1-t7Zc5r8UE4hlOc/VM/CzDv+pz4g="
684 | },
685 | "combined-stream": {
686 | "version": "0.0.7",
687 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz",
688 | "integrity": "sha1-ATfmV7qlp1QcV6w3rF/AfXO03B8=",
689 | "requires": {
690 | "delayed-stream": "0.0.5"
691 | }
692 | },
693 | "cryptiles": {
694 | "version": "2.0.5",
695 | "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz",
696 | "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=",
697 | "requires": {
698 | "boom": "2.10.1"
699 | }
700 | },
701 | "delayed-stream": {
702 | "version": "0.0.5",
703 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz",
704 | "integrity": "sha1-1LH0OpPoKW3+AmlPRoC8N6MTxz8="
705 | },
706 | "form-data": {
707 | "version": "0.2.0",
708 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.2.0.tgz",
709 | "integrity": "sha1-Jvi8JtpkQOKZy9z7aQNcT3em5GY=",
710 | "requires": {
711 | "async": "0.9.2",
712 | "combined-stream": "0.0.7",
713 | "mime-types": "2.0.14"
714 | }
715 | },
716 | "har-validator": {
717 | "version": "1.8.0",
718 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-1.8.0.tgz",
719 | "integrity": "sha1-2DhCsOtMQ1lgrrEIoGejqpTA7rI=",
720 | "requires": {
721 | "bluebird": "2.11.0",
722 | "chalk": "1.1.3",
723 | "commander": "2.14.1",
724 | "is-my-json-valid": "2.17.2"
725 | },
726 | "dependencies": {
727 | "chalk": {
728 | "version": "1.1.3",
729 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
730 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
731 | "requires": {
732 | "ansi-styles": "2.2.1",
733 | "escape-string-regexp": "1.0.5",
734 | "has-ansi": "2.0.0",
735 | "strip-ansi": "3.0.1",
736 | "supports-color": "2.0.0"
737 | }
738 | }
739 | }
740 | },
741 | "hawk": {
742 | "version": "2.3.1",
743 | "resolved": "https://registry.npmjs.org/hawk/-/hawk-2.3.1.tgz",
744 | "integrity": "sha1-HnMc45RH+h0PbXB/e87r7A/R7B8=",
745 | "requires": {
746 | "boom": "2.10.1",
747 | "cryptiles": "2.0.5",
748 | "hoek": "2.16.3",
749 | "sntp": "1.0.9"
750 | }
751 | },
752 | "hoek": {
753 | "version": "2.16.3",
754 | "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
755 | "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0="
756 | },
757 | "http-signature": {
758 | "version": "0.10.1",
759 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz",
760 | "integrity": "sha1-T72sEyVZqoMjEh5UB3nAoBKyfmY=",
761 | "requires": {
762 | "asn1": "0.1.11",
763 | "assert-plus": "0.1.5",
764 | "ctype": "0.5.3"
765 | }
766 | },
767 | "lodash": {
768 | "version": "3.6.0",
769 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.6.0.tgz",
770 | "integrity": "sha1-Umao9J3Zib5Pn2gbbyoMVShdDZo="
771 | },
772 | "mime-db": {
773 | "version": "1.12.0",
774 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.12.0.tgz",
775 | "integrity": "sha1-PQxjGA9FjrENMlqqN9fFiuMS6dc="
776 | },
777 | "mime-types": {
778 | "version": "2.0.14",
779 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.14.tgz",
780 | "integrity": "sha1-MQ4VnbI+B3+Lsit0jav6SVcUCqY=",
781 | "requires": {
782 | "mime-db": "1.12.0"
783 | }
784 | },
785 | "node-uuid": {
786 | "version": "1.4.8",
787 | "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz",
788 | "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc="
789 | },
790 | "oauth-sign": {
791 | "version": "0.6.0",
792 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.6.0.tgz",
793 | "integrity": "sha1-fb6uRPbKRU4fFoRR1jB0ZzWBPOM="
794 | },
795 | "pkginfo": {
796 | "version": "0.3.1",
797 | "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz",
798 | "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE="
799 | },
800 | "qs": {
801 | "version": "2.4.2",
802 | "resolved": "https://registry.npmjs.org/qs/-/qs-2.4.2.tgz",
803 | "integrity": "sha1-9854jld33wtQENp/fE5zujJHD1o="
804 | },
805 | "request": {
806 | "version": "2.54.0",
807 | "resolved": "https://registry.npmjs.org/request/-/request-2.54.0.tgz",
808 | "integrity": "sha1-oTkXzY6PpzMy2gvy+EowGB3vGVM=",
809 | "requires": {
810 | "aws-sign2": "0.5.0",
811 | "bl": "0.9.5",
812 | "caseless": "0.9.0",
813 | "combined-stream": "0.0.7",
814 | "forever-agent": "0.6.1",
815 | "form-data": "0.2.0",
816 | "har-validator": "1.8.0",
817 | "hawk": "2.3.1",
818 | "http-signature": "0.10.1",
819 | "isstream": "0.1.2",
820 | "json-stringify-safe": "5.0.1",
821 | "mime-types": "2.0.14",
822 | "node-uuid": "1.4.8",
823 | "oauth-sign": "0.6.0",
824 | "qs": "2.4.2",
825 | "stringstream": "0.0.5",
826 | "tough-cookie": "2.3.3",
827 | "tunnel-agent": "0.4.3"
828 | }
829 | },
830 | "sntp": {
831 | "version": "1.0.9",
832 | "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz",
833 | "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=",
834 | "requires": {
835 | "hoek": "2.16.3"
836 | }
837 | },
838 | "supports-color": {
839 | "version": "2.0.0",
840 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
841 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
842 | },
843 | "tunnel-agent": {
844 | "version": "0.4.3",
845 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz",
846 | "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us="
847 | },
848 | "winston": {
849 | "version": "0.9.0",
850 | "resolved": "https://registry.npmjs.org/winston/-/winston-0.9.0.tgz",
851 | "integrity": "sha1-tXJubEIpHjBeNihs566fO3SlJ6g=",
852 | "requires": {
853 | "async": "0.9.2",
854 | "colors": "1.0.3",
855 | "cycle": "1.0.3",
856 | "eyes": "0.1.8",
857 | "isstream": "0.1.2",
858 | "pkginfo": "0.3.1",
859 | "stack-trace": "0.0.10"
860 | }
861 | }
862 | }
863 | },
864 | "getpass": {
865 | "version": "0.1.7",
866 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
867 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
868 | "requires": {
869 | "assert-plus": "1.0.0"
870 | }
871 | },
872 | "glob": {
873 | "version": "7.1.2",
874 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
875 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
876 | "requires": {
877 | "fs.realpath": "1.0.0",
878 | "inflight": "1.0.6",
879 | "inherits": "2.0.3",
880 | "minimatch": "3.0.4",
881 | "once": "1.4.0",
882 | "path-is-absolute": "1.0.1"
883 | }
884 | },
885 | "graceful-fs": {
886 | "version": "4.1.11",
887 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
888 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
889 | },
890 | "har-schema": {
891 | "version": "2.0.0",
892 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
893 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
894 | },
895 | "har-validator": {
896 | "version": "5.0.3",
897 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
898 | "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
899 | "requires": {
900 | "ajv": "5.5.2",
901 | "har-schema": "2.0.0"
902 | }
903 | },
904 | "has-ansi": {
905 | "version": "2.0.0",
906 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
907 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
908 | "requires": {
909 | "ansi-regex": "2.1.1"
910 | }
911 | },
912 | "has-flag": {
913 | "version": "3.0.0",
914 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
915 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
916 | },
917 | "hawk": {
918 | "version": "6.0.2",
919 | "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz",
920 | "integrity": "sha1-r02RTrBl+bXOTZ0RwcshJu7MMDg=",
921 | "requires": {
922 | "boom": "4.3.1",
923 | "cryptiles": "3.1.2",
924 | "hoek": "4.2.0",
925 | "sntp": "2.1.0"
926 | }
927 | },
928 | "hoek": {
929 | "version": "4.2.0",
930 | "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz",
931 | "integrity": "sha1-ctnQdU9/4lyi0BrY+PmpRJqJUm0="
932 | },
933 | "hostile": {
934 | "version": "1.3.1",
935 | "resolved": "https://registry.npmjs.org/hostile/-/hostile-1.3.1.tgz",
936 | "integrity": "sha512-5AnCyKcIInErGgRGkZ6Cq3BhS0hck9Ruk2ZmRaULSCLdlQ1vcKuJuEu0w+6pkkQ3ZMgCY4k3ra8EmbsMW/JKAw==",
937 | "requires": {
938 | "chalk": "1.1.3",
939 | "minimist": "1.1.1",
940 | "once": "1.4.0",
941 | "split": "1.0.1",
942 | "through": "2.3.8"
943 | },
944 | "dependencies": {
945 | "ansi-styles": {
946 | "version": "2.2.1",
947 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
948 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
949 | },
950 | "chalk": {
951 | "version": "1.1.3",
952 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
953 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
954 | "requires": {
955 | "ansi-styles": "2.2.1",
956 | "escape-string-regexp": "1.0.5",
957 | "has-ansi": "2.0.0",
958 | "strip-ansi": "3.0.1",
959 | "supports-color": "2.0.0"
960 | }
961 | },
962 | "supports-color": {
963 | "version": "2.0.0",
964 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
965 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
966 | }
967 | }
968 | },
969 | "http-errors": {
970 | "version": "1.6.2",
971 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz",
972 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=",
973 | "requires": {
974 | "depd": "1.1.1",
975 | "inherits": "2.0.3",
976 | "setprototypeof": "1.0.3",
977 | "statuses": "1.3.1"
978 | },
979 | "dependencies": {
980 | "depd": {
981 | "version": "1.1.1",
982 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
983 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k="
984 | },
985 | "setprototypeof": {
986 | "version": "1.0.3",
987 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz",
988 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ="
989 | }
990 | }
991 | },
992 | "http-signature": {
993 | "version": "1.2.0",
994 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
995 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
996 | "requires": {
997 | "assert-plus": "1.0.0",
998 | "jsprim": "1.4.1",
999 | "sshpk": "1.13.1"
1000 | }
1001 | },
1002 | "iconv-lite": {
1003 | "version": "0.4.19",
1004 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
1005 | "integrity": "sha1-90aPYBNfXl2tM5nAqBvpoWA6CCs="
1006 | },
1007 | "inflight": {
1008 | "version": "1.0.6",
1009 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
1010 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
1011 | "requires": {
1012 | "once": "1.4.0",
1013 | "wrappy": "1.0.2"
1014 | }
1015 | },
1016 | "inherits": {
1017 | "version": "2.0.3",
1018 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
1019 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
1020 | },
1021 | "ipaddr.js": {
1022 | "version": "1.5.2",
1023 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz",
1024 | "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A="
1025 | },
1026 | "is-fullwidth-code-point": {
1027 | "version": "2.0.0",
1028 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
1029 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
1030 | },
1031 | "is-my-ip-valid": {
1032 | "version": "1.0.0",
1033 | "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz",
1034 | "integrity": "sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ=="
1035 | },
1036 | "is-my-json-valid": {
1037 | "version": "2.17.2",
1038 | "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz",
1039 | "integrity": "sha512-IBhBslgngMQN8DDSppmgDv7RNrlFotuuDsKcrCP3+HbFaVivIBU7u9oiiErw8sH4ynx3+gOGQ3q2otkgiSi6kg==",
1040 | "requires": {
1041 | "generate-function": "2.0.0",
1042 | "generate-object-property": "1.2.0",
1043 | "is-my-ip-valid": "1.0.0",
1044 | "jsonpointer": "4.0.1",
1045 | "xtend": "4.0.1"
1046 | }
1047 | },
1048 | "is-property": {
1049 | "version": "1.0.2",
1050 | "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
1051 | "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ="
1052 | },
1053 | "is-typedarray": {
1054 | "version": "1.0.0",
1055 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
1056 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
1057 | },
1058 | "isarray": {
1059 | "version": "0.0.1",
1060 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
1061 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
1062 | },
1063 | "isexe": {
1064 | "version": "2.0.0",
1065 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
1066 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
1067 | },
1068 | "isstream": {
1069 | "version": "0.1.2",
1070 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
1071 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
1072 | },
1073 | "jsbn": {
1074 | "version": "0.1.1",
1075 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
1076 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
1077 | "optional": true
1078 | },
1079 | "json-schema": {
1080 | "version": "0.2.3",
1081 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
1082 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
1083 | },
1084 | "json-schema-traverse": {
1085 | "version": "0.3.1",
1086 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
1087 | "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A="
1088 | },
1089 | "json-stringify-safe": {
1090 | "version": "5.0.1",
1091 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
1092 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
1093 | },
1094 | "jsonify": {
1095 | "version": "0.0.0",
1096 | "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
1097 | "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM="
1098 | },
1099 | "jsonpointer": {
1100 | "version": "4.0.1",
1101 | "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz",
1102 | "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk="
1103 | },
1104 | "jsprim": {
1105 | "version": "1.4.1",
1106 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
1107 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
1108 | "requires": {
1109 | "assert-plus": "1.0.0",
1110 | "extsprintf": "1.3.0",
1111 | "json-schema": "0.2.3",
1112 | "verror": "1.10.0"
1113 | }
1114 | },
1115 | "kind-of": {
1116 | "version": "5.1.0",
1117 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
1118 | "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="
1119 | },
1120 | "left-pad": {
1121 | "version": "1.2.0",
1122 | "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.2.0.tgz",
1123 | "integrity": "sha1-0wpzxrggHY99jnlWupYWCHpo4O4="
1124 | },
1125 | "lock": {
1126 | "version": "0.1.4",
1127 | "resolved": "https://registry.npmjs.org/lock/-/lock-0.1.4.tgz",
1128 | "integrity": "sha1-/sfervF+fDoKVeHaBCgD4l2RdF0="
1129 | },
1130 | "lodash": {
1131 | "version": "4.17.5",
1132 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz",
1133 | "integrity": "sha1-maktZcAnLevoyWtgV7yPv6O+1RE="
1134 | },
1135 | "longest": {
1136 | "version": "2.0.1",
1137 | "resolved": "https://registry.npmjs.org/longest/-/longest-2.0.1.tgz",
1138 | "integrity": "sha1-eB4YMpaqlPbU2RbcM10NF676I/g="
1139 | },
1140 | "media-typer": {
1141 | "version": "0.3.0",
1142 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
1143 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
1144 | },
1145 | "merge-descriptors": {
1146 | "version": "1.0.1",
1147 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
1148 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
1149 | },
1150 | "methods": {
1151 | "version": "1.1.2",
1152 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
1153 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
1154 | },
1155 | "mime": {
1156 | "version": "1.4.1",
1157 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
1158 | "integrity": "sha1-Eh+evEnjdm8xGnbh+hyAA8SwOqY="
1159 | },
1160 | "mime-db": {
1161 | "version": "1.30.0",
1162 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz",
1163 | "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE="
1164 | },
1165 | "mime-types": {
1166 | "version": "2.1.17",
1167 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz",
1168 | "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=",
1169 | "requires": {
1170 | "mime-db": "1.30.0"
1171 | }
1172 | },
1173 | "minimatch": {
1174 | "version": "3.0.4",
1175 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
1176 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
1177 | "requires": {
1178 | "brace-expansion": "1.1.11"
1179 | }
1180 | },
1181 | "minimist": {
1182 | "version": "1.1.1",
1183 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.1.1.tgz",
1184 | "integrity": "sha1-G8K8cWWM3KVxJHVoQ2NhWwtPaVs="
1185 | },
1186 | "mkpath": {
1187 | "version": "0.1.0",
1188 | "resolved": "https://registry.npmjs.org/mkpath/-/mkpath-0.1.0.tgz",
1189 | "integrity": "sha1-dVSm+Nhxg0zJe1RisSLEwSTW3pE="
1190 | },
1191 | "morgan": {
1192 | "version": "1.9.0",
1193 | "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz",
1194 | "integrity": "sha1-0B+mxlhZt2/PMbPLU6OCGjEdgFE=",
1195 | "requires": {
1196 | "basic-auth": "2.0.0",
1197 | "debug": "2.6.9",
1198 | "depd": "1.1.2",
1199 | "on-finished": "2.3.0",
1200 | "on-headers": "1.0.1"
1201 | }
1202 | },
1203 | "ms": {
1204 | "version": "2.0.0",
1205 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1206 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
1207 | },
1208 | "negotiator": {
1209 | "version": "0.6.1",
1210 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
1211 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
1212 | },
1213 | "ngrok": {
1214 | "version": "2.2.26",
1215 | "resolved": "https://registry.npmjs.org/ngrok/-/ngrok-2.2.26.tgz",
1216 | "integrity": "sha512-DAWmOerUpBvtjLnX+Da9iE5JFCCWXBqYVFwjQtllnveIHL+u/TRjtsYqg4zttkoXXceG+gGon92atDGQd2SgiA==",
1217 | "requires": {
1218 | "@types/node": "8.9.4",
1219 | "async": "2.6.0",
1220 | "decompress-zip": "0.3.0",
1221 | "lock": "0.1.4",
1222 | "request": "2.83.0",
1223 | "uuid": "3.2.1"
1224 | },
1225 | "dependencies": {
1226 | "async": {
1227 | "version": "2.6.0",
1228 | "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz",
1229 | "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==",
1230 | "requires": {
1231 | "lodash": "4.17.5"
1232 | }
1233 | }
1234 | }
1235 | },
1236 | "nice-try": {
1237 | "version": "1.0.4",
1238 | "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.4.tgz",
1239 | "integrity": "sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA=="
1240 | },
1241 | "nopt": {
1242 | "version": "3.0.6",
1243 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
1244 | "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
1245 | "requires": {
1246 | "abbrev": "1.1.1"
1247 | }
1248 | },
1249 | "npm-execspawn": {
1250 | "version": "1.3.0",
1251 | "resolved": "https://registry.npmjs.org/npm-execspawn/-/npm-execspawn-1.3.0.tgz",
1252 | "integrity": "sha1-W0hgqM94NGU4ajiKIaFF0/cSVjg=",
1253 | "requires": {
1254 | "debug": "2.6.9",
1255 | "execspawn": "1.0.1",
1256 | "shell-quote": "1.6.1",
1257 | "xtend": "3.0.0"
1258 | },
1259 | "dependencies": {
1260 | "xtend": {
1261 | "version": "3.0.0",
1262 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz",
1263 | "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo="
1264 | }
1265 | }
1266 | },
1267 | "oauth-sign": {
1268 | "version": "0.8.2",
1269 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
1270 | "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM="
1271 | },
1272 | "on-finished": {
1273 | "version": "2.3.0",
1274 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
1275 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
1276 | "requires": {
1277 | "ee-first": "1.1.1"
1278 | }
1279 | },
1280 | "on-headers": {
1281 | "version": "1.0.1",
1282 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz",
1283 | "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c="
1284 | },
1285 | "once": {
1286 | "version": "1.4.0",
1287 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
1288 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
1289 | "requires": {
1290 | "wrappy": "1.0.2"
1291 | }
1292 | },
1293 | "openurl": {
1294 | "version": "1.1.1",
1295 | "resolved": "https://registry.npmjs.org/openurl/-/openurl-1.1.1.tgz",
1296 | "integrity": "sha1-OHW0sO96UsFW8NtB1GCduw+Us4c="
1297 | },
1298 | "pad": {
1299 | "version": "2.0.3",
1300 | "resolved": "https://registry.npmjs.org/pad/-/pad-2.0.3.tgz",
1301 | "integrity": "sha512-YVlBmpDQilhUl69RY/0Ku9Il0sNPLgVOVePhCJUfN8qDZKrq1zIY+ZwtCLtaWFtJzuJswwAcZHxf4a5RliV2pQ==",
1302 | "requires": {
1303 | "wcwidth": "1.0.1"
1304 | }
1305 | },
1306 | "parseurl": {
1307 | "version": "1.3.2",
1308 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
1309 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
1310 | },
1311 | "path-is-absolute": {
1312 | "version": "1.0.1",
1313 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
1314 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
1315 | },
1316 | "path-key": {
1317 | "version": "2.0.1",
1318 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
1319 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
1320 | },
1321 | "path-to-regexp": {
1322 | "version": "0.1.7",
1323 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
1324 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
1325 | },
1326 | "performance-now": {
1327 | "version": "2.1.0",
1328 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
1329 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
1330 | },
1331 | "pkginfo": {
1332 | "version": "0.4.1",
1333 | "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.4.1.tgz",
1334 | "integrity": "sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8="
1335 | },
1336 | "proxy-addr": {
1337 | "version": "2.0.2",
1338 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz",
1339 | "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=",
1340 | "requires": {
1341 | "forwarded": "0.1.2",
1342 | "ipaddr.js": "1.5.2"
1343 | }
1344 | },
1345 | "punycode": {
1346 | "version": "1.4.1",
1347 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
1348 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
1349 | },
1350 | "q": {
1351 | "version": "1.2.0",
1352 | "resolved": "https://registry.npmjs.org/q/-/q-1.2.0.tgz",
1353 | "integrity": "sha1-gRcFzkqYAq3/gRqw/NvQGUbh/iI="
1354 | },
1355 | "qs": {
1356 | "version": "6.5.1",
1357 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
1358 | "integrity": "sha1-NJzfbu+J7EXBLX1es/wMhwNDptg="
1359 | },
1360 | "range-parser": {
1361 | "version": "1.2.0",
1362 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
1363 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
1364 | },
1365 | "raw-body": {
1366 | "version": "2.3.2",
1367 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz",
1368 | "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=",
1369 | "requires": {
1370 | "bytes": "3.0.0",
1371 | "http-errors": "1.6.2",
1372 | "iconv-lite": "0.4.19",
1373 | "unpipe": "1.0.0"
1374 | }
1375 | },
1376 | "readable-stream": {
1377 | "version": "1.0.34",
1378 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
1379 | "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
1380 | "requires": {
1381 | "core-util-is": "1.0.2",
1382 | "inherits": "2.0.3",
1383 | "isarray": "0.0.1",
1384 | "string_decoder": "0.10.31"
1385 | }
1386 | },
1387 | "repeat-string": {
1388 | "version": "1.6.1",
1389 | "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
1390 | "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
1391 | },
1392 | "request": {
1393 | "version": "2.83.0",
1394 | "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz",
1395 | "integrity": "sha1-ygtl2gLtYpNYh4COb1EDgQNOM1Y=",
1396 | "requires": {
1397 | "aws-sign2": "0.7.0",
1398 | "aws4": "1.6.0",
1399 | "caseless": "0.12.0",
1400 | "combined-stream": "1.0.5",
1401 | "extend": "3.0.1",
1402 | "forever-agent": "0.6.1",
1403 | "form-data": "2.3.1",
1404 | "har-validator": "5.0.3",
1405 | "hawk": "6.0.2",
1406 | "http-signature": "1.2.0",
1407 | "is-typedarray": "1.0.0",
1408 | "isstream": "0.1.2",
1409 | "json-stringify-safe": "5.0.1",
1410 | "mime-types": "2.1.17",
1411 | "oauth-sign": "0.8.2",
1412 | "performance-now": "2.1.0",
1413 | "qs": "6.5.1",
1414 | "safe-buffer": "5.1.1",
1415 | "stringstream": "0.0.5",
1416 | "tough-cookie": "2.3.3",
1417 | "tunnel-agent": "0.6.0",
1418 | "uuid": "3.2.1"
1419 | }
1420 | },
1421 | "request-promise": {
1422 | "version": "4.2.2",
1423 | "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.2.tgz",
1424 | "integrity": "sha1-0epG1lSm7k+O5qT+oQGMIpEZBLQ=",
1425 | "requires": {
1426 | "bluebird": "3.5.1",
1427 | "request-promise-core": "1.1.1",
1428 | "stealthy-require": "1.1.1",
1429 | "tough-cookie": "2.3.3"
1430 | }
1431 | },
1432 | "request-promise-core": {
1433 | "version": "1.1.1",
1434 | "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz",
1435 | "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=",
1436 | "requires": {
1437 | "lodash": "4.17.5"
1438 | }
1439 | },
1440 | "require-dir": {
1441 | "version": "0.3.0",
1442 | "resolved": "https://registry.npmjs.org/require-dir/-/require-dir-0.3.0.tgz",
1443 | "integrity": "sha1-ifB0qFY4sHwgpPuUyTtdtjWmR4E="
1444 | },
1445 | "safe-buffer": {
1446 | "version": "5.1.1",
1447 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
1448 | "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM="
1449 | },
1450 | "sax": {
1451 | "version": "1.2.4",
1452 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
1453 | "integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk="
1454 | },
1455 | "semver": {
1456 | "version": "4.3.1",
1457 | "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.1.tgz",
1458 | "integrity": "sha1-vrASlXW5X3YRCymvCNNw/Z7rNL8="
1459 | },
1460 | "send": {
1461 | "version": "0.16.1",
1462 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz",
1463 | "integrity": "sha1-pw4coh0TgsEdDZ9iMd6ygQgNerM=",
1464 | "requires": {
1465 | "debug": "2.6.9",
1466 | "depd": "1.1.2",
1467 | "destroy": "1.0.4",
1468 | "encodeurl": "1.0.2",
1469 | "escape-html": "1.0.3",
1470 | "etag": "1.8.1",
1471 | "fresh": "0.5.2",
1472 | "http-errors": "1.6.2",
1473 | "mime": "1.4.1",
1474 | "ms": "2.0.0",
1475 | "on-finished": "2.3.0",
1476 | "range-parser": "1.2.0",
1477 | "statuses": "1.3.1"
1478 | }
1479 | },
1480 | "serve-static": {
1481 | "version": "1.13.1",
1482 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz",
1483 | "integrity": "sha1-TFfVNASnYdjy58HooYpH2/J4pxk=",
1484 | "requires": {
1485 | "encodeurl": "1.0.2",
1486 | "escape-html": "1.0.3",
1487 | "parseurl": "1.3.2",
1488 | "send": "0.16.1"
1489 | }
1490 | },
1491 | "setprototypeof": {
1492 | "version": "1.1.0",
1493 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
1494 | "integrity": "sha1-0L2FU2iHtv58DYGMuWLZ2RxU5lY="
1495 | },
1496 | "shebang-command": {
1497 | "version": "1.2.0",
1498 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
1499 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
1500 | "requires": {
1501 | "shebang-regex": "1.0.0"
1502 | }
1503 | },
1504 | "shebang-regex": {
1505 | "version": "1.0.0",
1506 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
1507 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
1508 | },
1509 | "shell-quote": {
1510 | "version": "1.6.1",
1511 | "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz",
1512 | "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=",
1513 | "requires": {
1514 | "array-filter": "0.0.1",
1515 | "array-map": "0.0.0",
1516 | "array-reduce": "0.0.0",
1517 | "jsonify": "0.0.0"
1518 | }
1519 | },
1520 | "slice-ansi": {
1521 | "version": "1.0.0",
1522 | "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz",
1523 | "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==",
1524 | "requires": {
1525 | "is-fullwidth-code-point": "2.0.0"
1526 | }
1527 | },
1528 | "sntp": {
1529 | "version": "2.1.0",
1530 | "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz",
1531 | "integrity": "sha1-LGzsFP7cIiJznK+bXD2F0cxaLMg=",
1532 | "requires": {
1533 | "hoek": "4.2.0"
1534 | }
1535 | },
1536 | "split": {
1537 | "version": "1.0.1",
1538 | "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
1539 | "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==",
1540 | "requires": {
1541 | "through": "2.3.8"
1542 | }
1543 | },
1544 | "sprintf-js": {
1545 | "version": "1.0.3",
1546 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
1547 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
1548 | },
1549 | "sshpk": {
1550 | "version": "1.13.1",
1551 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz",
1552 | "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=",
1553 | "requires": {
1554 | "asn1": "0.2.3",
1555 | "assert-plus": "1.0.0",
1556 | "bcrypt-pbkdf": "1.0.1",
1557 | "dashdash": "1.14.1",
1558 | "ecc-jsbn": "0.1.1",
1559 | "getpass": "0.1.7",
1560 | "jsbn": "0.1.1",
1561 | "tweetnacl": "0.14.5"
1562 | }
1563 | },
1564 | "stack-trace": {
1565 | "version": "0.0.10",
1566 | "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
1567 | "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA="
1568 | },
1569 | "statuses": {
1570 | "version": "1.3.1",
1571 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
1572 | "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4="
1573 | },
1574 | "stealthy-require": {
1575 | "version": "1.1.1",
1576 | "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
1577 | "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks="
1578 | },
1579 | "string-width": {
1580 | "version": "2.1.1",
1581 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
1582 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
1583 | "requires": {
1584 | "is-fullwidth-code-point": "2.0.0",
1585 | "strip-ansi": "4.0.0"
1586 | },
1587 | "dependencies": {
1588 | "ansi-regex": {
1589 | "version": "3.0.0",
1590 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
1591 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
1592 | },
1593 | "strip-ansi": {
1594 | "version": "4.0.0",
1595 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
1596 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
1597 | "requires": {
1598 | "ansi-regex": "3.0.0"
1599 | }
1600 | }
1601 | }
1602 | },
1603 | "string_decoder": {
1604 | "version": "0.10.31",
1605 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
1606 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
1607 | },
1608 | "stringstream": {
1609 | "version": "0.0.5",
1610 | "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
1611 | "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg="
1612 | },
1613 | "strip-ansi": {
1614 | "version": "3.0.1",
1615 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
1616 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
1617 | "requires": {
1618 | "ansi-regex": "2.1.1"
1619 | }
1620 | },
1621 | "supports-color": {
1622 | "version": "5.2.0",
1623 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz",
1624 | "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==",
1625 | "requires": {
1626 | "has-flag": "3.0.0"
1627 | }
1628 | },
1629 | "table": {
1630 | "version": "4.0.2",
1631 | "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz",
1632 | "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==",
1633 | "requires": {
1634 | "ajv": "5.5.2",
1635 | "ajv-keywords": "2.1.1",
1636 | "chalk": "2.3.1",
1637 | "lodash": "4.17.5",
1638 | "slice-ansi": "1.0.0",
1639 | "string-width": "2.1.1"
1640 | }
1641 | },
1642 | "through": {
1643 | "version": "2.3.8",
1644 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
1645 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
1646 | },
1647 | "touch": {
1648 | "version": "0.0.3",
1649 | "resolved": "https://registry.npmjs.org/touch/-/touch-0.0.3.tgz",
1650 | "integrity": "sha1-Ua7z1ElXHU8oel2Hyci0kYGg2x0=",
1651 | "requires": {
1652 | "nopt": "1.0.10"
1653 | },
1654 | "dependencies": {
1655 | "nopt": {
1656 | "version": "1.0.10",
1657 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
1658 | "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=",
1659 | "requires": {
1660 | "abbrev": "1.1.1"
1661 | }
1662 | }
1663 | }
1664 | },
1665 | "tough-cookie": {
1666 | "version": "2.3.3",
1667 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz",
1668 | "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=",
1669 | "requires": {
1670 | "punycode": "1.4.1"
1671 | }
1672 | },
1673 | "traverse": {
1674 | "version": "0.3.9",
1675 | "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz",
1676 | "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk="
1677 | },
1678 | "tunnel-agent": {
1679 | "version": "0.6.0",
1680 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
1681 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
1682 | "requires": {
1683 | "safe-buffer": "5.1.1"
1684 | }
1685 | },
1686 | "tweetnacl": {
1687 | "version": "0.14.5",
1688 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
1689 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
1690 | "optional": true
1691 | },
1692 | "type-is": {
1693 | "version": "1.6.15",
1694 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz",
1695 | "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=",
1696 | "requires": {
1697 | "media-typer": "0.3.0",
1698 | "mime-types": "2.1.17"
1699 | }
1700 | },
1701 | "unpipe": {
1702 | "version": "1.0.0",
1703 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
1704 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
1705 | },
1706 | "util-extend": {
1707 | "version": "1.0.3",
1708 | "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz",
1709 | "integrity": "sha1-p8IW0mdUUWljeztu3GypEZ4v+T8="
1710 | },
1711 | "utils-merge": {
1712 | "version": "1.0.1",
1713 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
1714 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
1715 | },
1716 | "uuid": {
1717 | "version": "3.2.1",
1718 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz",
1719 | "integrity": "sha1-EsUou51Y0LkmXZovbw/ovhf/HxQ="
1720 | },
1721 | "vary": {
1722 | "version": "1.1.2",
1723 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
1724 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
1725 | },
1726 | "verror": {
1727 | "version": "1.10.0",
1728 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
1729 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
1730 | "requires": {
1731 | "assert-plus": "1.0.0",
1732 | "core-util-is": "1.0.2",
1733 | "extsprintf": "1.3.0"
1734 | }
1735 | },
1736 | "wcwidth": {
1737 | "version": "1.0.1",
1738 | "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
1739 | "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=",
1740 | "requires": {
1741 | "defaults": "1.0.3"
1742 | }
1743 | },
1744 | "which": {
1745 | "version": "1.3.0",
1746 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz",
1747 | "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==",
1748 | "requires": {
1749 | "isexe": "2.0.0"
1750 | }
1751 | },
1752 | "wordwrap": {
1753 | "version": "1.0.0",
1754 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
1755 | "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
1756 | },
1757 | "wrappy": {
1758 | "version": "1.0.2",
1759 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
1760 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
1761 | },
1762 | "xml-js": {
1763 | "version": "1.6.2",
1764 | "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.2.tgz",
1765 | "integrity": "sha1-TEy4QTmY9zcBogKhuLLxfJhacsU=",
1766 | "requires": {
1767 | "sax": "1.2.4"
1768 | }
1769 | },
1770 | "xtend": {
1771 | "version": "4.0.1",
1772 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
1773 | "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
1774 | },
1775 | "yamljs": {
1776 | "version": "0.3.0",
1777 | "resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz",
1778 | "integrity": "sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ==",
1779 | "requires": {
1780 | "argparse": "1.0.9",
1781 | "glob": "7.1.2"
1782 | }
1783 | }
1784 | }
1785 | }
1786 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vmix-rest-api",
3 | "version": "0.4.0",
4 | "description": "",
5 | "main": "app/index.js",
6 | "bin": "app/index.js",
7 | "dependencies": {
8 | "ascii-table": "0.0.9",
9 | "body-parser": "^1.18.2",
10 | "boolean": "^0.1.3",
11 | "center-align": "^1.0.1",
12 | "chalk": "^2.3.1",
13 | "clear": "0.0.1",
14 | "cross-spawn": "^6.0.4",
15 | "express": "^4.16.2",
16 | "express-http-proxy": "^1.1.0",
17 | "figlet": "^1.2.0",
18 | "get-version": "^1.1.3",
19 | "hostile": "^1.3.1",
20 | "left-pad": "^1.2.0",
21 | "lodash": "^4.17.5",
22 | "morgan": "^1.9.0",
23 | "ngrok": "^2.2.26",
24 | "npm-execspawn": "^1.3.0",
25 | "openurl": "^1.1.1",
26 | "pad": "^2.0.3",
27 | "pkginfo": "^0.4.1",
28 | "request": "^2.83.0",
29 | "request-promise": "^4.2.2",
30 | "table": "^4.0.2",
31 | "wordwrap": "^1.0.0",
32 | "xml-js": "^1.6.2",
33 | "yamljs": "^0.3.0"
34 | },
35 | "devDependencies": {},
36 | "scripts": {
37 | "package": "scripts/package.sh",
38 | "package-nozip": "scripts/package-nozip.sh",
39 | "version": "echo $npm_package_version"
40 | },
41 | "repository": {
42 | "type": "git",
43 | "url": "git+ssh://git@gitlab.com/curtgrimes/vmix-rest-api.git"
44 | },
45 | "pkg": {},
46 | "author": "",
47 | "license": "ISC",
48 | "bugs": {
49 | "url": "https://gitlab.com/curtgrimes/vmix-rest-api/issues"
50 | },
51 | "homepage": "https://gitlab.com/curtgrimes/vmix-rest-api#README"
52 | }
53 |
--------------------------------------------------------------------------------
/scripts/package-nozip.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | VERSION=`npm run -s version`
3 | EXE_FILENAME="vmix-rest-api-$(echo $VERSION).exe"
4 | pkg --debug --targets latest-win-x64 --output $(echo $EXE_FILENAME) app/index.js
--------------------------------------------------------------------------------
/scripts/package.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | VERSION=`npm run -s version`
3 | EXE_FILENAME="vmix-rest-api-$(echo $VERSION).exe"
4 | mkdir .tmp .tmp/lib
5 | rm -f vmix-rest-api.zip # if it already exists
6 |
7 | pkg --targets latest-win-x64 --output .tmp/$(echo $EXE_FILENAME) app/index.js
8 | cp config/config.sample.yml .tmp
9 | mv .tmp/config.sample.yml .tmp/config.yml
10 |
11 | curl https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-windows-amd64.zip > .tmp/ngrok.zip
12 |
13 | unzip .tmp/ngrok.zip -d .tmp/
14 | ls .tmp
15 | rm .tmp/ngrok.zip
16 | mv .tmp/ngrok.exe .tmp/lib/ngrok.exe
17 |
18 | cd .tmp
19 | zip -r vmix-rest-api-$(echo $VERSION)-windows.zip .
20 | mv vmix-rest-api-$(echo $VERSION)-windows.zip ..
21 | cd ..
22 | rm -rf .tmp
--------------------------------------------------------------------------------