├── .editorconfig
├── .gitignore
├── LICENSE
├── README.md
├── client
├── .env.example
├── .eslintrc
├── README.md
├── package-lock.json
├── package.json
├── public
│ ├── index.html
│ ├── manifest.json
│ └── robots.txt
└── src
│ ├── App.js
│ ├── index.js
│ └── styles.css
├── package-lock.json
├── package.json
├── server
├── .eslintrc.js
├── README.md
├── package-lock.json
├── package.json
├── serverless.yml
├── src
│ ├── graphql.js
│ ├── models
│ │ ├── Client.js
│ │ └── Topic.js
│ ├── publish.js
│ ├── utils
│ │ ├── dynamodb.js
│ │ ├── publish.js
│ │ └── subscribe.js
│ └── websocket.js
└── webpack.config.js
└── showtime.gif
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: http://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | [*]
7 | indent_style = tab
8 | indent_size = 2
9 | end_of_line = lf
10 | charset = utf-8
11 | insert_final_newline = true
12 | max_line_length = 100
13 |
14 | [*.yml]
15 | indent_style = spaces
16 | indent_size = 2
17 |
18 | [*.md]
19 | trim_trailing_whitespace = false
20 |
21 | [*.js]
22 | trim_trailing_whitespace = true
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | client/build
13 |
14 | # misc
15 | .DS_Store
16 | .env
17 | .env.local
18 | .env.development.local
19 | .env.test.local
20 | .env.production.local
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
26 | # Serverless directories
27 | server/.serverless
28 | server/.dynamodb
29 | server/.webpack
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 AlpacaGoesCrazy
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Apollo server subscriptions on AWS Lambda
2 |
3 | This is an example application which shows how to implement apollo-server-lambda with subscriptions.
4 | For subscriptions implementation AWS API Gateway Websockets and DynamoDB were used
5 |
6 | 
7 |
--------------------------------------------------------------------------------
/client/.env.example:
--------------------------------------------------------------------------------
1 | REACT_APP_SUBSCRIPTION_ENDPOINT="ws://localhost:3001"
2 | REACT_APP_HTTP_ENDPOINT="http://localhost:3000/graphql"
3 |
--------------------------------------------------------------------------------
/client/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "react-app",
3 | "env": {
4 | "browser": true,
5 | "node": true,
6 | "es6": true
7 | },
8 | "globals": {
9 | "nconf": true
10 | },
11 | "parser": "babel-eslint",
12 | "plugins": ["react"],
13 |
14 | "parserOptions": {
15 | "ecmaVersion": 6,
16 | "sourceType": "module",
17 | },
18 | "rules": {
19 | "indent": [
20 | "error",
21 | "tab",
22 | { "SwitchCase": 1 }
23 | ],
24 | "linebreak-style": [
25 | "error",
26 | "unix"
27 | ],
28 | "quotes": [
29 | "error",
30 | "single"
31 | ],
32 | "semi": [
33 | "error",
34 | "never"
35 | ],
36 | "no-console": "error"
37 | }
38 | }
--------------------------------------------------------------------------------
/client/README.md:
--------------------------------------------------------------------------------
1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
2 |
3 | ## Available Scripts
4 |
5 | In the project directory, you can run:
6 |
7 | ### `npm start`
8 |
9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
11 |
12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console.
14 |
15 | ### `npm test`
16 |
17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
19 |
20 | ### `npm run build`
21 |
22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance.
24 |
25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed!
27 |
28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
29 |
30 | ### `npm run eject`
31 |
32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
33 |
34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
35 |
36 | Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
37 |
38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
39 |
40 | ## Learn More
41 |
42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
43 |
44 | To learn React, check out the [React documentation](https://reactjs.org/).
45 |
46 | ### Code Splitting
47 |
48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
49 |
50 | ### Analyzing the Bundle Size
51 |
52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
53 |
54 | ### Making a Progressive Web App
55 |
56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
57 |
58 | ### Advanced Configuration
59 |
60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
61 |
62 | ### Deployment
63 |
64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
65 |
66 | ### `npm run build` fails to minify
67 |
68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
69 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "version": "1.0.0",
4 | "dependencies": {
5 | "@apollo/react-hooks": "^3.1.0",
6 | "@material-ui/core": "^4.4.2",
7 | "apollo-cache-inmemory": "^1.6.3",
8 | "apollo-client": "^2.6.4",
9 | "apollo-link": "^1.2.13",
10 | "apollo-link-http": "^1.5.16",
11 | "apollo-link-ws": "^1.0.19",
12 | "apollo-utilities": "^1.3.2",
13 | "graphql-tag": "^2.10.1",
14 | "react": "^16.9.0",
15 | "react-dom": "^16.9.0",
16 | "react-scripts": "3.1.1",
17 | "subscriptions-transport-ws": "^0.9.16"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "browserslist": {
26 | "production": [
27 | ">0.2%",
28 | "not dead",
29 | "not op_mini all"
30 | ],
31 | "development": [
32 | "last 1 chrome version",
33 | "last 1 firefox version",
34 | "last 1 safari version"
35 | ]
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/client/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 | Serverless Websockets
13 |
14 |
15 | You need to enable JavaScript to run this app.
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/client/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Serverless Websockets",
3 | "name": "Serverless Websockets",
4 | "display": "standalone",
5 | "theme_color": "#000000",
6 | "background_color": "#ffffff"
7 | }
8 |
--------------------------------------------------------------------------------
/client/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 |
--------------------------------------------------------------------------------
/client/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useState, useRef } from 'react'
2 | import { useSubscription, useMutation } from '@apollo/react-hooks'
3 | import gql from 'graphql-tag'
4 |
5 | import TextField from '@material-ui/core/TextField'
6 | import Button from '@material-ui/core/Button'
7 | import List from '@material-ui/core/List'
8 | import ListItem from '@material-ui/core/ListItem'
9 |
10 | import './styles.css'
11 |
12 | const listenMessageSubscription = gql`
13 | subscription listenMessage {
14 | listenMessage
15 | }
16 | `
17 | const sendMessageMutation = gql`
18 | mutation sendMessage($message: String!) {
19 | sendMessage(message: $message)
20 | }
21 | `
22 |
23 | function App() {
24 | const [messages, setMessages] = useState([])
25 | const [input, setInput] = useState('')
26 | const [username, setUsername] = useState('Anonymous')
27 | const messageBoxRef = useRef()
28 |
29 | const onSubscriptionData = ({ subscriptionData: { data: { listenMessage } } }) => {
30 | setMessages(messages => [...messages, listenMessage])
31 | messageBoxRef.current.scrollTop = messageBoxRef.current.scrollHeight
32 | }
33 |
34 | useSubscription(listenMessageSubscription, { onSubscriptionData })
35 | const [sendMessage] = useMutation(sendMessageMutation)
36 |
37 | const handleSend = () => {
38 | if(input) {
39 | sendMessage({ variables: { message: `${username}: ${input}` } })
40 | setInput('')
41 | }
42 | }
43 |
44 | const handleKeyPress = e => {
45 | if(e.charCode === 13) {
46 | handleSend()
47 | }
48 | }
49 |
50 | return (
51 |
52 |
53 | {messages.map((message, i) =>
54 |
55 | {message}
56 |
57 | )}
58 |
59 |
60 | setUsername(e.target.value)}
65 | />
66 | setInput(e.target.value)}
74 |
75 | />
76 |
77 | SEND
78 |
79 |
80 |
81 | )
82 | }
83 |
84 | export default App
85 |
--------------------------------------------------------------------------------
/client/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import { ApolloClient } from 'apollo-client'
4 | import { ApolloProvider } from '@apollo/react-hooks'
5 | import { split } from 'apollo-link'
6 | import { HttpLink } from 'apollo-link-http'
7 | import { WebSocketLink } from 'apollo-link-ws'
8 | import { SubscriptionClient } from 'subscriptions-transport-ws'
9 | import { getMainDefinition } from 'apollo-utilities'
10 | import { InMemoryCache } from 'apollo-cache-inmemory'
11 |
12 | import App from './App'
13 |
14 | const http_endpoint = process.env.REACT_APP_HTTP_ENDPOINT
15 | const ws_endpoint = process.env.REACT_APP_SUBSCRIPTION_ENDPOINT
16 |
17 | const subClient = new SubscriptionClient(ws_endpoint, { lazy: true, reconnect: true }, null, [])
18 | const wsLink = new WebSocketLink(subClient)
19 | const httpLink = new HttpLink({ uri: http_endpoint })
20 |
21 | const link = split(
22 | ({ query }) => {
23 | const definition = getMainDefinition(query)
24 | return (
25 | definition.kind === 'OperationDefinition' &&
26 | definition.operation === 'subscription'
27 | )
28 | },
29 | wsLink,
30 | httpLink,
31 | )
32 |
33 | const client = new ApolloClient({
34 | cache: new InMemoryCache(),
35 | link
36 | })
37 |
38 | ReactDOM.render(
39 | (
40 |
41 | ),
42 | document.getElementById('root')
43 | )
44 |
45 |
46 |
--------------------------------------------------------------------------------
/client/src/styles.css:
--------------------------------------------------------------------------------
1 | * { box-sizing: border-box; }
2 | body {
3 | margin: 0;
4 | padding: 10px;
5 | height: 100vh;
6 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
7 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
8 | sans-serif;
9 | -webkit-font-smoothing: antialiased;
10 | -moz-osx-font-smoothing: grayscale;
11 | }
12 |
13 | #root {
14 | height: 100%;
15 | display: flex;
16 | flex-direction: column;
17 | }
18 |
19 | .message-container {
20 | overflow: auto;
21 | border: solid 1px #000;
22 | margin-bottom: 10px;
23 | flex-grow: 2;
24 | }
25 |
26 | .message-container li {
27 | border-bottom: 1px solid rgba(0,0,0,.2);
28 | }
29 |
30 | .control-panel {
31 | display: flex;
32 | margin-top: 10px;
33 | }
34 |
35 | .control-panel div {
36 | margin-right: 10px;
37 | }
38 |
39 | .input-message {
40 | flex-grow: 2
41 | }
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "serverless-graphql-subscriptions",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@babel/code-frame": {
8 | "version": "7.5.5",
9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz",
10 | "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==",
11 | "dev": true,
12 | "requires": {
13 | "@babel/highlight": "^7.0.0"
14 | }
15 | },
16 | "@babel/highlight": {
17 | "version": "7.5.0",
18 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz",
19 | "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==",
20 | "dev": true,
21 | "requires": {
22 | "chalk": "^2.0.0",
23 | "esutils": "^2.0.2",
24 | "js-tokens": "^4.0.0"
25 | }
26 | },
27 | "@babel/runtime": {
28 | "version": "7.6.0",
29 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz",
30 | "integrity": "sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==",
31 | "dev": true,
32 | "requires": {
33 | "regenerator-runtime": "^0.13.2"
34 | }
35 | },
36 | "acorn": {
37 | "version": "7.1.1",
38 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz",
39 | "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==",
40 | "dev": true
41 | },
42 | "acorn-jsx": {
43 | "version": "5.0.2",
44 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.2.tgz",
45 | "integrity": "sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw==",
46 | "dev": true
47 | },
48 | "ajv": {
49 | "version": "6.10.2",
50 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
51 | "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
52 | "dev": true,
53 | "requires": {
54 | "fast-deep-equal": "^2.0.1",
55 | "fast-json-stable-stringify": "^2.0.0",
56 | "json-schema-traverse": "^0.4.1",
57 | "uri-js": "^4.2.2"
58 | }
59 | },
60 | "ansi-escapes": {
61 | "version": "3.2.0",
62 | "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
63 | "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
64 | "dev": true
65 | },
66 | "ansi-regex": {
67 | "version": "3.0.0",
68 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
69 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
70 | "dev": true
71 | },
72 | "ansi-styles": {
73 | "version": "3.2.1",
74 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
75 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
76 | "dev": true,
77 | "requires": {
78 | "color-convert": "^1.9.0"
79 | }
80 | },
81 | "argparse": {
82 | "version": "1.0.10",
83 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
84 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
85 | "dev": true,
86 | "requires": {
87 | "sprintf-js": "~1.0.2"
88 | }
89 | },
90 | "aria-query": {
91 | "version": "3.0.0",
92 | "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz",
93 | "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=",
94 | "dev": true,
95 | "requires": {
96 | "ast-types-flow": "0.0.7",
97 | "commander": "^2.11.0"
98 | }
99 | },
100 | "array-includes": {
101 | "version": "3.0.3",
102 | "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz",
103 | "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=",
104 | "dev": true,
105 | "requires": {
106 | "define-properties": "^1.1.2",
107 | "es-abstract": "^1.7.0"
108 | }
109 | },
110 | "ast-types-flow": {
111 | "version": "0.0.7",
112 | "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
113 | "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=",
114 | "dev": true
115 | },
116 | "astral-regex": {
117 | "version": "1.0.0",
118 | "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
119 | "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
120 | "dev": true
121 | },
122 | "axobject-query": {
123 | "version": "2.0.2",
124 | "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz",
125 | "integrity": "sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww==",
126 | "dev": true,
127 | "requires": {
128 | "ast-types-flow": "0.0.7"
129 | }
130 | },
131 | "balanced-match": {
132 | "version": "1.0.0",
133 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
134 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
135 | "dev": true
136 | },
137 | "brace-expansion": {
138 | "version": "1.1.11",
139 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
140 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
141 | "dev": true,
142 | "requires": {
143 | "balanced-match": "^1.0.0",
144 | "concat-map": "0.0.1"
145 | }
146 | },
147 | "callsites": {
148 | "version": "3.1.0",
149 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
150 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
151 | "dev": true
152 | },
153 | "chalk": {
154 | "version": "2.4.2",
155 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
156 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
157 | "dev": true,
158 | "requires": {
159 | "ansi-styles": "^3.2.1",
160 | "escape-string-regexp": "^1.0.5",
161 | "supports-color": "^5.3.0"
162 | }
163 | },
164 | "chardet": {
165 | "version": "0.7.0",
166 | "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
167 | "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
168 | "dev": true
169 | },
170 | "cli-cursor": {
171 | "version": "2.1.0",
172 | "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
173 | "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
174 | "dev": true,
175 | "requires": {
176 | "restore-cursor": "^2.0.0"
177 | }
178 | },
179 | "cli-width": {
180 | "version": "2.2.0",
181 | "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
182 | "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
183 | "dev": true
184 | },
185 | "color-convert": {
186 | "version": "1.9.3",
187 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
188 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
189 | "dev": true,
190 | "requires": {
191 | "color-name": "1.1.3"
192 | }
193 | },
194 | "color-name": {
195 | "version": "1.1.3",
196 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
197 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
198 | "dev": true
199 | },
200 | "commander": {
201 | "version": "2.20.0",
202 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
203 | "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
204 | "dev": true
205 | },
206 | "concat-map": {
207 | "version": "0.0.1",
208 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
209 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
210 | "dev": true
211 | },
212 | "contains-path": {
213 | "version": "0.1.0",
214 | "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
215 | "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
216 | "dev": true
217 | },
218 | "cross-spawn": {
219 | "version": "6.0.5",
220 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
221 | "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
222 | "dev": true,
223 | "requires": {
224 | "nice-try": "^1.0.4",
225 | "path-key": "^2.0.1",
226 | "semver": "^5.5.0",
227 | "shebang-command": "^1.2.0",
228 | "which": "^1.2.9"
229 | }
230 | },
231 | "damerau-levenshtein": {
232 | "version": "1.0.5",
233 | "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.5.tgz",
234 | "integrity": "sha512-CBCRqFnpu715iPmw1KrdOrzRqbdFwQTwAWyyyYS42+iAgHCuXZ+/TdMgQkUENPomxEz9z1BEzuQU2Xw0kUuAgA==",
235 | "dev": true
236 | },
237 | "debug": {
238 | "version": "4.1.1",
239 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
240 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
241 | "dev": true,
242 | "requires": {
243 | "ms": "^2.1.1"
244 | }
245 | },
246 | "deep-is": {
247 | "version": "0.1.3",
248 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
249 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
250 | "dev": true
251 | },
252 | "define-properties": {
253 | "version": "1.1.3",
254 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
255 | "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
256 | "dev": true,
257 | "requires": {
258 | "object-keys": "^1.0.12"
259 | }
260 | },
261 | "doctrine": {
262 | "version": "3.0.0",
263 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
264 | "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
265 | "dev": true,
266 | "requires": {
267 | "esutils": "^2.0.2"
268 | }
269 | },
270 | "emoji-regex": {
271 | "version": "7.0.3",
272 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
273 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
274 | "dev": true
275 | },
276 | "error-ex": {
277 | "version": "1.3.2",
278 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
279 | "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
280 | "dev": true,
281 | "requires": {
282 | "is-arrayish": "^0.2.1"
283 | }
284 | },
285 | "es-abstract": {
286 | "version": "1.14.2",
287 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.14.2.tgz",
288 | "integrity": "sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg==",
289 | "dev": true,
290 | "requires": {
291 | "es-to-primitive": "^1.2.0",
292 | "function-bind": "^1.1.1",
293 | "has": "^1.0.3",
294 | "has-symbols": "^1.0.0",
295 | "is-callable": "^1.1.4",
296 | "is-regex": "^1.0.4",
297 | "object-inspect": "^1.6.0",
298 | "object-keys": "^1.1.1",
299 | "string.prototype.trimleft": "^2.0.0",
300 | "string.prototype.trimright": "^2.0.0"
301 | }
302 | },
303 | "es-to-primitive": {
304 | "version": "1.2.0",
305 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz",
306 | "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==",
307 | "dev": true,
308 | "requires": {
309 | "is-callable": "^1.1.4",
310 | "is-date-object": "^1.0.1",
311 | "is-symbol": "^1.0.2"
312 | }
313 | },
314 | "escape-string-regexp": {
315 | "version": "1.0.5",
316 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
317 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
318 | "dev": true
319 | },
320 | "eslint": {
321 | "version": "6.1.0",
322 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.1.0.tgz",
323 | "integrity": "sha512-QhrbdRD7ofuV09IuE2ySWBz0FyXCq0rriLTZXZqaWSI79CVtHVRdkFuFTViiqzZhkCgfOh9USpriuGN2gIpZDQ==",
324 | "dev": true,
325 | "requires": {
326 | "@babel/code-frame": "^7.0.0",
327 | "ajv": "^6.10.0",
328 | "chalk": "^2.1.0",
329 | "cross-spawn": "^6.0.5",
330 | "debug": "^4.0.1",
331 | "doctrine": "^3.0.0",
332 | "eslint-scope": "^5.0.0",
333 | "eslint-utils": "^1.3.1",
334 | "eslint-visitor-keys": "^1.0.0",
335 | "espree": "^6.0.0",
336 | "esquery": "^1.0.1",
337 | "esutils": "^2.0.2",
338 | "file-entry-cache": "^5.0.1",
339 | "functional-red-black-tree": "^1.0.1",
340 | "glob-parent": "^5.0.0",
341 | "globals": "^11.7.0",
342 | "ignore": "^4.0.6",
343 | "import-fresh": "^3.0.0",
344 | "imurmurhash": "^0.1.4",
345 | "inquirer": "^6.4.1",
346 | "is-glob": "^4.0.0",
347 | "js-yaml": "^3.13.1",
348 | "json-stable-stringify-without-jsonify": "^1.0.1",
349 | "levn": "^0.3.0",
350 | "lodash": "^4.17.14",
351 | "minimatch": "^3.0.4",
352 | "mkdirp": "^0.5.1",
353 | "natural-compare": "^1.4.0",
354 | "optionator": "^0.8.2",
355 | "progress": "^2.0.0",
356 | "regexpp": "^2.0.1",
357 | "semver": "^6.1.2",
358 | "strip-ansi": "^5.2.0",
359 | "strip-json-comments": "^3.0.1",
360 | "table": "^5.2.3",
361 | "text-table": "^0.2.0",
362 | "v8-compile-cache": "^2.0.3"
363 | },
364 | "dependencies": {
365 | "semver": {
366 | "version": "6.3.0",
367 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
368 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
369 | "dev": true
370 | }
371 | }
372 | },
373 | "eslint-import-resolver-node": {
374 | "version": "0.3.2",
375 | "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz",
376 | "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==",
377 | "dev": true,
378 | "requires": {
379 | "debug": "^2.6.9",
380 | "resolve": "^1.5.0"
381 | },
382 | "dependencies": {
383 | "debug": {
384 | "version": "2.6.9",
385 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
386 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
387 | "dev": true,
388 | "requires": {
389 | "ms": "2.0.0"
390 | }
391 | },
392 | "ms": {
393 | "version": "2.0.0",
394 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
395 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
396 | "dev": true
397 | }
398 | }
399 | },
400 | "eslint-module-utils": {
401 | "version": "2.4.1",
402 | "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.4.1.tgz",
403 | "integrity": "sha512-H6DOj+ejw7Tesdgbfs4jeS4YMFrT8uI8xwd1gtQqXssaR0EQ26L+2O/w6wkYFy2MymON0fTwHmXBvvfLNZVZEw==",
404 | "dev": true,
405 | "requires": {
406 | "debug": "^2.6.8",
407 | "pkg-dir": "^2.0.0"
408 | },
409 | "dependencies": {
410 | "debug": {
411 | "version": "2.6.9",
412 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
413 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
414 | "dev": true,
415 | "requires": {
416 | "ms": "2.0.0"
417 | }
418 | },
419 | "ms": {
420 | "version": "2.0.0",
421 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
422 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
423 | "dev": true
424 | }
425 | }
426 | },
427 | "eslint-plugin-flowtype": {
428 | "version": "4.3.0",
429 | "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-4.3.0.tgz",
430 | "integrity": "sha512-elvqoadMHnYqSYN1YXn02DR7SFW8Kc2CLe8na3m2GdQPQhIY+BgCd2quVJ1AbW3aO0zcyE9loVJ7Szy8A/xlMA==",
431 | "dev": true,
432 | "requires": {
433 | "lodash": "^4.17.15"
434 | }
435 | },
436 | "eslint-plugin-import": {
437 | "version": "2.17.2",
438 | "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.17.2.tgz",
439 | "integrity": "sha512-m+cSVxM7oLsIpmwNn2WXTJoReOF9f/CtLMo7qOVmKd1KntBy0hEcuNZ3erTmWjx+DxRO0Zcrm5KwAvI9wHcV5g==",
440 | "dev": true,
441 | "requires": {
442 | "array-includes": "^3.0.3",
443 | "contains-path": "^0.1.0",
444 | "debug": "^2.6.9",
445 | "doctrine": "1.5.0",
446 | "eslint-import-resolver-node": "^0.3.2",
447 | "eslint-module-utils": "^2.4.0",
448 | "has": "^1.0.3",
449 | "lodash": "^4.17.11",
450 | "minimatch": "^3.0.4",
451 | "read-pkg-up": "^2.0.0",
452 | "resolve": "^1.10.0"
453 | },
454 | "dependencies": {
455 | "debug": {
456 | "version": "2.6.9",
457 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
458 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
459 | "dev": true,
460 | "requires": {
461 | "ms": "2.0.0"
462 | }
463 | },
464 | "doctrine": {
465 | "version": "1.5.0",
466 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
467 | "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
468 | "dev": true,
469 | "requires": {
470 | "esutils": "^2.0.2",
471 | "isarray": "^1.0.0"
472 | }
473 | },
474 | "ms": {
475 | "version": "2.0.0",
476 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
477 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
478 | "dev": true
479 | }
480 | }
481 | },
482 | "eslint-plugin-jsx-a11y": {
483 | "version": "6.2.3",
484 | "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.3.tgz",
485 | "integrity": "sha512-CawzfGt9w83tyuVekn0GDPU9ytYtxyxyFZ3aSWROmnRRFQFT2BiPJd7jvRdzNDi6oLWaS2asMeYSNMjWTV4eNg==",
486 | "dev": true,
487 | "requires": {
488 | "@babel/runtime": "^7.4.5",
489 | "aria-query": "^3.0.0",
490 | "array-includes": "^3.0.3",
491 | "ast-types-flow": "^0.0.7",
492 | "axobject-query": "^2.0.2",
493 | "damerau-levenshtein": "^1.0.4",
494 | "emoji-regex": "^7.0.2",
495 | "has": "^1.0.3",
496 | "jsx-ast-utils": "^2.2.1"
497 | }
498 | },
499 | "eslint-plugin-react": {
500 | "version": "7.12.4",
501 | "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.12.4.tgz",
502 | "integrity": "sha512-1puHJkXJY+oS1t467MjbqjvX53uQ05HXwjqDgdbGBqf5j9eeydI54G3KwiJmWciQ0HTBacIKw2jgwSBSH3yfgQ==",
503 | "dev": true,
504 | "requires": {
505 | "array-includes": "^3.0.3",
506 | "doctrine": "^2.1.0",
507 | "has": "^1.0.3",
508 | "jsx-ast-utils": "^2.0.1",
509 | "object.fromentries": "^2.0.0",
510 | "prop-types": "^15.6.2",
511 | "resolve": "^1.9.0"
512 | },
513 | "dependencies": {
514 | "doctrine": {
515 | "version": "2.1.0",
516 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
517 | "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
518 | "dev": true,
519 | "requires": {
520 | "esutils": "^2.0.2"
521 | }
522 | }
523 | }
524 | },
525 | "eslint-plugin-react-hooks": {
526 | "version": "2.0.1",
527 | "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-2.0.1.tgz",
528 | "integrity": "sha512-xir+3KHKo86AasxlCV8AHRtIZPHljqCRRUYgASkbatmt0fad4+5GgC7zkT7o/06hdKM6MIwp8giHVXqBPaarHQ==",
529 | "dev": true
530 | },
531 | "eslint-scope": {
532 | "version": "5.0.0",
533 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz",
534 | "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==",
535 | "dev": true,
536 | "requires": {
537 | "esrecurse": "^4.1.0",
538 | "estraverse": "^4.1.1"
539 | }
540 | },
541 | "eslint-utils": {
542 | "version": "1.4.2",
543 | "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz",
544 | "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==",
545 | "dev": true,
546 | "requires": {
547 | "eslint-visitor-keys": "^1.0.0"
548 | }
549 | },
550 | "eslint-visitor-keys": {
551 | "version": "1.1.0",
552 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz",
553 | "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==",
554 | "dev": true
555 | },
556 | "espree": {
557 | "version": "6.1.1",
558 | "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.1.tgz",
559 | "integrity": "sha512-EYbr8XZUhWbYCqQRW0duU5LxzL5bETN6AjKBGy1302qqzPaCH10QbRg3Wvco79Z8x9WbiE8HYB4e75xl6qUYvQ==",
560 | "dev": true,
561 | "requires": {
562 | "acorn": "^7.0.0",
563 | "acorn-jsx": "^5.0.2",
564 | "eslint-visitor-keys": "^1.1.0"
565 | }
566 | },
567 | "esprima": {
568 | "version": "4.0.1",
569 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
570 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
571 | "dev": true
572 | },
573 | "esquery": {
574 | "version": "1.0.1",
575 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz",
576 | "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==",
577 | "dev": true,
578 | "requires": {
579 | "estraverse": "^4.0.0"
580 | }
581 | },
582 | "esrecurse": {
583 | "version": "4.2.1",
584 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
585 | "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
586 | "dev": true,
587 | "requires": {
588 | "estraverse": "^4.1.0"
589 | }
590 | },
591 | "estraverse": {
592 | "version": "4.3.0",
593 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
594 | "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
595 | "dev": true
596 | },
597 | "esutils": {
598 | "version": "2.0.3",
599 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
600 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
601 | "dev": true
602 | },
603 | "external-editor": {
604 | "version": "3.1.0",
605 | "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
606 | "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
607 | "dev": true,
608 | "requires": {
609 | "chardet": "^0.7.0",
610 | "iconv-lite": "^0.4.24",
611 | "tmp": "^0.0.33"
612 | }
613 | },
614 | "fast-deep-equal": {
615 | "version": "2.0.1",
616 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
617 | "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
618 | "dev": true
619 | },
620 | "fast-json-stable-stringify": {
621 | "version": "2.0.0",
622 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
623 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
624 | "dev": true
625 | },
626 | "fast-levenshtein": {
627 | "version": "2.0.6",
628 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
629 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
630 | "dev": true
631 | },
632 | "figures": {
633 | "version": "2.0.0",
634 | "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
635 | "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
636 | "dev": true,
637 | "requires": {
638 | "escape-string-regexp": "^1.0.5"
639 | }
640 | },
641 | "file-entry-cache": {
642 | "version": "5.0.1",
643 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
644 | "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
645 | "dev": true,
646 | "requires": {
647 | "flat-cache": "^2.0.1"
648 | }
649 | },
650 | "find-up": {
651 | "version": "2.1.0",
652 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
653 | "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
654 | "dev": true,
655 | "requires": {
656 | "locate-path": "^2.0.0"
657 | }
658 | },
659 | "flat-cache": {
660 | "version": "2.0.1",
661 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
662 | "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
663 | "dev": true,
664 | "requires": {
665 | "flatted": "^2.0.0",
666 | "rimraf": "2.6.3",
667 | "write": "1.0.3"
668 | }
669 | },
670 | "flatted": {
671 | "version": "2.0.1",
672 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz",
673 | "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==",
674 | "dev": true
675 | },
676 | "fs.realpath": {
677 | "version": "1.0.0",
678 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
679 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
680 | "dev": true
681 | },
682 | "function-bind": {
683 | "version": "1.1.1",
684 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
685 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
686 | "dev": true
687 | },
688 | "functional-red-black-tree": {
689 | "version": "1.0.1",
690 | "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
691 | "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
692 | "dev": true
693 | },
694 | "glob": {
695 | "version": "7.1.4",
696 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
697 | "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
698 | "dev": true,
699 | "requires": {
700 | "fs.realpath": "^1.0.0",
701 | "inflight": "^1.0.4",
702 | "inherits": "2",
703 | "minimatch": "^3.0.4",
704 | "once": "^1.3.0",
705 | "path-is-absolute": "^1.0.0"
706 | }
707 | },
708 | "glob-parent": {
709 | "version": "5.1.2",
710 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
711 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
712 | "dev": true,
713 | "requires": {
714 | "is-glob": "^4.0.1"
715 | }
716 | },
717 | "globals": {
718 | "version": "11.12.0",
719 | "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
720 | "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
721 | "dev": true
722 | },
723 | "graceful-fs": {
724 | "version": "4.2.2",
725 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz",
726 | "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==",
727 | "dev": true
728 | },
729 | "has": {
730 | "version": "1.0.3",
731 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
732 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
733 | "dev": true,
734 | "requires": {
735 | "function-bind": "^1.1.1"
736 | }
737 | },
738 | "has-flag": {
739 | "version": "3.0.0",
740 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
741 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
742 | "dev": true
743 | },
744 | "has-symbols": {
745 | "version": "1.0.0",
746 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
747 | "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
748 | "dev": true
749 | },
750 | "hosted-git-info": {
751 | "version": "2.8.9",
752 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
753 | "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
754 | "dev": true
755 | },
756 | "iconv-lite": {
757 | "version": "0.4.24",
758 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
759 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
760 | "dev": true,
761 | "requires": {
762 | "safer-buffer": ">= 2.1.2 < 3"
763 | }
764 | },
765 | "ignore": {
766 | "version": "4.0.6",
767 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
768 | "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
769 | "dev": true
770 | },
771 | "import-fresh": {
772 | "version": "3.1.0",
773 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz",
774 | "integrity": "sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==",
775 | "dev": true,
776 | "requires": {
777 | "parent-module": "^1.0.0",
778 | "resolve-from": "^4.0.0"
779 | }
780 | },
781 | "imurmurhash": {
782 | "version": "0.1.4",
783 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
784 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
785 | "dev": true
786 | },
787 | "inflight": {
788 | "version": "1.0.6",
789 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
790 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
791 | "dev": true,
792 | "requires": {
793 | "once": "^1.3.0",
794 | "wrappy": "1"
795 | }
796 | },
797 | "inherits": {
798 | "version": "2.0.4",
799 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
800 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
801 | "dev": true
802 | },
803 | "inquirer": {
804 | "version": "6.5.2",
805 | "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz",
806 | "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==",
807 | "dev": true,
808 | "requires": {
809 | "ansi-escapes": "^3.2.0",
810 | "chalk": "^2.4.2",
811 | "cli-cursor": "^2.1.0",
812 | "cli-width": "^2.0.0",
813 | "external-editor": "^3.0.3",
814 | "figures": "^2.0.0",
815 | "lodash": "^4.17.12",
816 | "mute-stream": "0.0.7",
817 | "run-async": "^2.2.0",
818 | "rxjs": "^6.4.0",
819 | "string-width": "^2.1.0",
820 | "strip-ansi": "^5.1.0",
821 | "through": "^2.3.6"
822 | }
823 | },
824 | "is-arrayish": {
825 | "version": "0.2.1",
826 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
827 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
828 | "dev": true
829 | },
830 | "is-callable": {
831 | "version": "1.1.4",
832 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
833 | "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==",
834 | "dev": true
835 | },
836 | "is-date-object": {
837 | "version": "1.0.1",
838 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
839 | "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
840 | "dev": true
841 | },
842 | "is-extglob": {
843 | "version": "2.1.1",
844 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
845 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
846 | "dev": true
847 | },
848 | "is-fullwidth-code-point": {
849 | "version": "2.0.0",
850 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
851 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
852 | "dev": true
853 | },
854 | "is-glob": {
855 | "version": "4.0.1",
856 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
857 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
858 | "dev": true,
859 | "requires": {
860 | "is-extglob": "^2.1.1"
861 | }
862 | },
863 | "is-promise": {
864 | "version": "2.1.0",
865 | "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
866 | "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
867 | "dev": true
868 | },
869 | "is-regex": {
870 | "version": "1.0.4",
871 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
872 | "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
873 | "dev": true,
874 | "requires": {
875 | "has": "^1.0.1"
876 | }
877 | },
878 | "is-symbol": {
879 | "version": "1.0.2",
880 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
881 | "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
882 | "dev": true,
883 | "requires": {
884 | "has-symbols": "^1.0.0"
885 | }
886 | },
887 | "isarray": {
888 | "version": "1.0.0",
889 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
890 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
891 | "dev": true
892 | },
893 | "isexe": {
894 | "version": "2.0.0",
895 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
896 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
897 | "dev": true
898 | },
899 | "js-tokens": {
900 | "version": "4.0.0",
901 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
902 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
903 | "dev": true
904 | },
905 | "js-yaml": {
906 | "version": "3.13.1",
907 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
908 | "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
909 | "dev": true,
910 | "requires": {
911 | "argparse": "^1.0.7",
912 | "esprima": "^4.0.0"
913 | }
914 | },
915 | "json-schema-traverse": {
916 | "version": "0.4.1",
917 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
918 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
919 | "dev": true
920 | },
921 | "json-stable-stringify-without-jsonify": {
922 | "version": "1.0.1",
923 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
924 | "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
925 | "dev": true
926 | },
927 | "jsx-ast-utils": {
928 | "version": "2.2.1",
929 | "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.1.tgz",
930 | "integrity": "sha512-v3FxCcAf20DayI+uxnCuw795+oOIkVu6EnJ1+kSzhqqTZHNkTZ7B66ZgLp4oLJ/gbA64cI0B7WRoHZMSRdyVRQ==",
931 | "dev": true,
932 | "requires": {
933 | "array-includes": "^3.0.3",
934 | "object.assign": "^4.1.0"
935 | }
936 | },
937 | "levn": {
938 | "version": "0.3.0",
939 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
940 | "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
941 | "dev": true,
942 | "requires": {
943 | "prelude-ls": "~1.1.2",
944 | "type-check": "~0.3.2"
945 | }
946 | },
947 | "load-json-file": {
948 | "version": "2.0.0",
949 | "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
950 | "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
951 | "dev": true,
952 | "requires": {
953 | "graceful-fs": "^4.1.2",
954 | "parse-json": "^2.2.0",
955 | "pify": "^2.0.0",
956 | "strip-bom": "^3.0.0"
957 | }
958 | },
959 | "locate-path": {
960 | "version": "2.0.0",
961 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
962 | "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
963 | "dev": true,
964 | "requires": {
965 | "p-locate": "^2.0.0",
966 | "path-exists": "^3.0.0"
967 | }
968 | },
969 | "lodash": {
970 | "version": "4.17.21",
971 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
972 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
973 | "dev": true
974 | },
975 | "loose-envify": {
976 | "version": "1.4.0",
977 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
978 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
979 | "dev": true,
980 | "requires": {
981 | "js-tokens": "^3.0.0 || ^4.0.0"
982 | }
983 | },
984 | "mimic-fn": {
985 | "version": "1.2.0",
986 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
987 | "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
988 | "dev": true
989 | },
990 | "minimatch": {
991 | "version": "3.0.4",
992 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
993 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
994 | "dev": true,
995 | "requires": {
996 | "brace-expansion": "^1.1.7"
997 | }
998 | },
999 | "minimist": {
1000 | "version": "0.0.8",
1001 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
1002 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
1003 | "dev": true
1004 | },
1005 | "mkdirp": {
1006 | "version": "0.5.1",
1007 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
1008 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
1009 | "dev": true,
1010 | "requires": {
1011 | "minimist": "0.0.8"
1012 | }
1013 | },
1014 | "ms": {
1015 | "version": "2.1.2",
1016 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
1017 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
1018 | "dev": true
1019 | },
1020 | "mute-stream": {
1021 | "version": "0.0.7",
1022 | "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
1023 | "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
1024 | "dev": true
1025 | },
1026 | "natural-compare": {
1027 | "version": "1.4.0",
1028 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
1029 | "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
1030 | "dev": true
1031 | },
1032 | "nice-try": {
1033 | "version": "1.0.5",
1034 | "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
1035 | "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
1036 | "dev": true
1037 | },
1038 | "normalize-package-data": {
1039 | "version": "2.5.0",
1040 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
1041 | "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
1042 | "dev": true,
1043 | "requires": {
1044 | "hosted-git-info": "^2.1.4",
1045 | "resolve": "^1.10.0",
1046 | "semver": "2 || 3 || 4 || 5",
1047 | "validate-npm-package-license": "^3.0.1"
1048 | }
1049 | },
1050 | "object-assign": {
1051 | "version": "4.1.1",
1052 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
1053 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
1054 | "dev": true
1055 | },
1056 | "object-inspect": {
1057 | "version": "1.6.0",
1058 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz",
1059 | "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==",
1060 | "dev": true
1061 | },
1062 | "object-keys": {
1063 | "version": "1.1.1",
1064 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
1065 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
1066 | "dev": true
1067 | },
1068 | "object.assign": {
1069 | "version": "4.1.0",
1070 | "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
1071 | "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
1072 | "dev": true,
1073 | "requires": {
1074 | "define-properties": "^1.1.2",
1075 | "function-bind": "^1.1.1",
1076 | "has-symbols": "^1.0.0",
1077 | "object-keys": "^1.0.11"
1078 | }
1079 | },
1080 | "object.fromentries": {
1081 | "version": "2.0.0",
1082 | "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.0.tgz",
1083 | "integrity": "sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA==",
1084 | "dev": true,
1085 | "requires": {
1086 | "define-properties": "^1.1.2",
1087 | "es-abstract": "^1.11.0",
1088 | "function-bind": "^1.1.1",
1089 | "has": "^1.0.1"
1090 | }
1091 | },
1092 | "once": {
1093 | "version": "1.4.0",
1094 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
1095 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
1096 | "dev": true,
1097 | "requires": {
1098 | "wrappy": "1"
1099 | }
1100 | },
1101 | "onetime": {
1102 | "version": "2.0.1",
1103 | "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
1104 | "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
1105 | "dev": true,
1106 | "requires": {
1107 | "mimic-fn": "^1.0.0"
1108 | }
1109 | },
1110 | "optionator": {
1111 | "version": "0.8.2",
1112 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
1113 | "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
1114 | "dev": true,
1115 | "requires": {
1116 | "deep-is": "~0.1.3",
1117 | "fast-levenshtein": "~2.0.4",
1118 | "levn": "~0.3.0",
1119 | "prelude-ls": "~1.1.2",
1120 | "type-check": "~0.3.2",
1121 | "wordwrap": "~1.0.0"
1122 | }
1123 | },
1124 | "os-tmpdir": {
1125 | "version": "1.0.2",
1126 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
1127 | "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
1128 | "dev": true
1129 | },
1130 | "p-limit": {
1131 | "version": "1.3.0",
1132 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
1133 | "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
1134 | "dev": true,
1135 | "requires": {
1136 | "p-try": "^1.0.0"
1137 | }
1138 | },
1139 | "p-locate": {
1140 | "version": "2.0.0",
1141 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
1142 | "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
1143 | "dev": true,
1144 | "requires": {
1145 | "p-limit": "^1.1.0"
1146 | }
1147 | },
1148 | "p-try": {
1149 | "version": "1.0.0",
1150 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
1151 | "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
1152 | "dev": true
1153 | },
1154 | "parent-module": {
1155 | "version": "1.0.1",
1156 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
1157 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
1158 | "dev": true,
1159 | "requires": {
1160 | "callsites": "^3.0.0"
1161 | }
1162 | },
1163 | "parse-json": {
1164 | "version": "2.2.0",
1165 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
1166 | "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
1167 | "dev": true,
1168 | "requires": {
1169 | "error-ex": "^1.2.0"
1170 | }
1171 | },
1172 | "path-exists": {
1173 | "version": "3.0.0",
1174 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
1175 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
1176 | "dev": true
1177 | },
1178 | "path-is-absolute": {
1179 | "version": "1.0.1",
1180 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
1181 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
1182 | "dev": true
1183 | },
1184 | "path-key": {
1185 | "version": "2.0.1",
1186 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
1187 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
1188 | "dev": true
1189 | },
1190 | "path-parse": {
1191 | "version": "1.0.7",
1192 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
1193 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
1194 | "dev": true
1195 | },
1196 | "path-type": {
1197 | "version": "2.0.0",
1198 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
1199 | "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
1200 | "dev": true,
1201 | "requires": {
1202 | "pify": "^2.0.0"
1203 | }
1204 | },
1205 | "pify": {
1206 | "version": "2.3.0",
1207 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
1208 | "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
1209 | "dev": true
1210 | },
1211 | "pkg-dir": {
1212 | "version": "2.0.0",
1213 | "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
1214 | "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
1215 | "dev": true,
1216 | "requires": {
1217 | "find-up": "^2.1.0"
1218 | }
1219 | },
1220 | "prelude-ls": {
1221 | "version": "1.1.2",
1222 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
1223 | "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
1224 | "dev": true
1225 | },
1226 | "progress": {
1227 | "version": "2.0.3",
1228 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
1229 | "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
1230 | "dev": true
1231 | },
1232 | "prop-types": {
1233 | "version": "15.7.2",
1234 | "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
1235 | "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
1236 | "dev": true,
1237 | "requires": {
1238 | "loose-envify": "^1.4.0",
1239 | "object-assign": "^4.1.1",
1240 | "react-is": "^16.8.1"
1241 | }
1242 | },
1243 | "punycode": {
1244 | "version": "2.1.1",
1245 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
1246 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
1247 | "dev": true
1248 | },
1249 | "react-is": {
1250 | "version": "16.9.0",
1251 | "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz",
1252 | "integrity": "sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw==",
1253 | "dev": true
1254 | },
1255 | "read-pkg": {
1256 | "version": "2.0.0",
1257 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
1258 | "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
1259 | "dev": true,
1260 | "requires": {
1261 | "load-json-file": "^2.0.0",
1262 | "normalize-package-data": "^2.3.2",
1263 | "path-type": "^2.0.0"
1264 | }
1265 | },
1266 | "read-pkg-up": {
1267 | "version": "2.0.0",
1268 | "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
1269 | "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
1270 | "dev": true,
1271 | "requires": {
1272 | "find-up": "^2.0.0",
1273 | "read-pkg": "^2.0.0"
1274 | }
1275 | },
1276 | "regenerator-runtime": {
1277 | "version": "0.13.3",
1278 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
1279 | "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==",
1280 | "dev": true
1281 | },
1282 | "regexpp": {
1283 | "version": "2.0.1",
1284 | "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
1285 | "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
1286 | "dev": true
1287 | },
1288 | "resolve": {
1289 | "version": "1.12.0",
1290 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz",
1291 | "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==",
1292 | "dev": true,
1293 | "requires": {
1294 | "path-parse": "^1.0.6"
1295 | }
1296 | },
1297 | "resolve-from": {
1298 | "version": "4.0.0",
1299 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
1300 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
1301 | "dev": true
1302 | },
1303 | "restore-cursor": {
1304 | "version": "2.0.0",
1305 | "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
1306 | "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
1307 | "dev": true,
1308 | "requires": {
1309 | "onetime": "^2.0.0",
1310 | "signal-exit": "^3.0.2"
1311 | }
1312 | },
1313 | "rimraf": {
1314 | "version": "2.6.3",
1315 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
1316 | "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
1317 | "dev": true,
1318 | "requires": {
1319 | "glob": "^7.1.3"
1320 | }
1321 | },
1322 | "run-async": {
1323 | "version": "2.3.0",
1324 | "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
1325 | "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
1326 | "dev": true,
1327 | "requires": {
1328 | "is-promise": "^2.1.0"
1329 | }
1330 | },
1331 | "rxjs": {
1332 | "version": "6.5.3",
1333 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz",
1334 | "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==",
1335 | "dev": true,
1336 | "requires": {
1337 | "tslib": "^1.9.0"
1338 | }
1339 | },
1340 | "safer-buffer": {
1341 | "version": "2.1.2",
1342 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
1343 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
1344 | "dev": true
1345 | },
1346 | "semver": {
1347 | "version": "5.7.1",
1348 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
1349 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
1350 | "dev": true
1351 | },
1352 | "shebang-command": {
1353 | "version": "1.2.0",
1354 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
1355 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
1356 | "dev": true,
1357 | "requires": {
1358 | "shebang-regex": "^1.0.0"
1359 | }
1360 | },
1361 | "shebang-regex": {
1362 | "version": "1.0.0",
1363 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
1364 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
1365 | "dev": true
1366 | },
1367 | "signal-exit": {
1368 | "version": "3.0.2",
1369 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
1370 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
1371 | "dev": true
1372 | },
1373 | "slice-ansi": {
1374 | "version": "2.1.0",
1375 | "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
1376 | "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
1377 | "dev": true,
1378 | "requires": {
1379 | "ansi-styles": "^3.2.0",
1380 | "astral-regex": "^1.0.0",
1381 | "is-fullwidth-code-point": "^2.0.0"
1382 | }
1383 | },
1384 | "spdx-correct": {
1385 | "version": "3.1.0",
1386 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
1387 | "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
1388 | "dev": true,
1389 | "requires": {
1390 | "spdx-expression-parse": "^3.0.0",
1391 | "spdx-license-ids": "^3.0.0"
1392 | }
1393 | },
1394 | "spdx-exceptions": {
1395 | "version": "2.2.0",
1396 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
1397 | "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==",
1398 | "dev": true
1399 | },
1400 | "spdx-expression-parse": {
1401 | "version": "3.0.0",
1402 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
1403 | "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
1404 | "dev": true,
1405 | "requires": {
1406 | "spdx-exceptions": "^2.1.0",
1407 | "spdx-license-ids": "^3.0.0"
1408 | }
1409 | },
1410 | "spdx-license-ids": {
1411 | "version": "3.0.5",
1412 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
1413 | "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==",
1414 | "dev": true
1415 | },
1416 | "sprintf-js": {
1417 | "version": "1.0.3",
1418 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
1419 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
1420 | "dev": true
1421 | },
1422 | "string-width": {
1423 | "version": "2.1.1",
1424 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
1425 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
1426 | "dev": true,
1427 | "requires": {
1428 | "is-fullwidth-code-point": "^2.0.0",
1429 | "strip-ansi": "^4.0.0"
1430 | },
1431 | "dependencies": {
1432 | "strip-ansi": {
1433 | "version": "4.0.0",
1434 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
1435 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
1436 | "dev": true,
1437 | "requires": {
1438 | "ansi-regex": "^3.0.0"
1439 | }
1440 | }
1441 | }
1442 | },
1443 | "string.prototype.trimleft": {
1444 | "version": "2.1.0",
1445 | "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz",
1446 | "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==",
1447 | "dev": true,
1448 | "requires": {
1449 | "define-properties": "^1.1.3",
1450 | "function-bind": "^1.1.1"
1451 | }
1452 | },
1453 | "string.prototype.trimright": {
1454 | "version": "2.1.0",
1455 | "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz",
1456 | "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==",
1457 | "dev": true,
1458 | "requires": {
1459 | "define-properties": "^1.1.3",
1460 | "function-bind": "^1.1.1"
1461 | }
1462 | },
1463 | "strip-ansi": {
1464 | "version": "5.2.0",
1465 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
1466 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
1467 | "dev": true,
1468 | "requires": {
1469 | "ansi-regex": "^4.1.0"
1470 | },
1471 | "dependencies": {
1472 | "ansi-regex": {
1473 | "version": "4.1.0",
1474 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
1475 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
1476 | "dev": true
1477 | }
1478 | }
1479 | },
1480 | "strip-bom": {
1481 | "version": "3.0.0",
1482 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
1483 | "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
1484 | "dev": true
1485 | },
1486 | "strip-json-comments": {
1487 | "version": "3.0.1",
1488 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz",
1489 | "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==",
1490 | "dev": true
1491 | },
1492 | "supports-color": {
1493 | "version": "5.5.0",
1494 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
1495 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
1496 | "dev": true,
1497 | "requires": {
1498 | "has-flag": "^3.0.0"
1499 | }
1500 | },
1501 | "table": {
1502 | "version": "5.4.6",
1503 | "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
1504 | "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==",
1505 | "dev": true,
1506 | "requires": {
1507 | "ajv": "^6.10.2",
1508 | "lodash": "^4.17.14",
1509 | "slice-ansi": "^2.1.0",
1510 | "string-width": "^3.0.0"
1511 | },
1512 | "dependencies": {
1513 | "string-width": {
1514 | "version": "3.1.0",
1515 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
1516 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
1517 | "dev": true,
1518 | "requires": {
1519 | "emoji-regex": "^7.0.1",
1520 | "is-fullwidth-code-point": "^2.0.0",
1521 | "strip-ansi": "^5.1.0"
1522 | }
1523 | }
1524 | }
1525 | },
1526 | "text-table": {
1527 | "version": "0.2.0",
1528 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
1529 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
1530 | "dev": true
1531 | },
1532 | "through": {
1533 | "version": "2.3.8",
1534 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
1535 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
1536 | "dev": true
1537 | },
1538 | "tmp": {
1539 | "version": "0.0.33",
1540 | "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
1541 | "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
1542 | "dev": true,
1543 | "requires": {
1544 | "os-tmpdir": "~1.0.2"
1545 | }
1546 | },
1547 | "tslib": {
1548 | "version": "1.10.0",
1549 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
1550 | "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==",
1551 | "dev": true
1552 | },
1553 | "type-check": {
1554 | "version": "0.3.2",
1555 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
1556 | "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
1557 | "dev": true,
1558 | "requires": {
1559 | "prelude-ls": "~1.1.2"
1560 | }
1561 | },
1562 | "uri-js": {
1563 | "version": "4.2.2",
1564 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
1565 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
1566 | "dev": true,
1567 | "requires": {
1568 | "punycode": "^2.1.0"
1569 | }
1570 | },
1571 | "v8-compile-cache": {
1572 | "version": "2.1.0",
1573 | "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz",
1574 | "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==",
1575 | "dev": true
1576 | },
1577 | "validate-npm-package-license": {
1578 | "version": "3.0.4",
1579 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
1580 | "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
1581 | "dev": true,
1582 | "requires": {
1583 | "spdx-correct": "^3.0.0",
1584 | "spdx-expression-parse": "^3.0.0"
1585 | }
1586 | },
1587 | "which": {
1588 | "version": "1.3.1",
1589 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
1590 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
1591 | "dev": true,
1592 | "requires": {
1593 | "isexe": "^2.0.0"
1594 | }
1595 | },
1596 | "wordwrap": {
1597 | "version": "1.0.0",
1598 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
1599 | "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
1600 | "dev": true
1601 | },
1602 | "wrappy": {
1603 | "version": "1.0.2",
1604 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
1605 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
1606 | "dev": true
1607 | },
1608 | "write": {
1609 | "version": "1.0.3",
1610 | "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
1611 | "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
1612 | "dev": true,
1613 | "requires": {
1614 | "mkdirp": "^0.5.1"
1615 | }
1616 | }
1617 | }
1618 | }
1619 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "serverless-graphql-subscriptions",
3 | "version": "1.0.0",
4 | "description": "Example of graphql subscriptions application done using aws lambda and aws websockets api gateway",
5 | "author": "github.com/AlpacaGoesCrazy",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "devDependencies": {
10 | "eslint": "^6.1.0",
11 | "eslint-plugin-flowtype": "^4.3.0",
12 | "eslint-plugin-import": "2.17.2",
13 | "eslint-plugin-jsx-a11y": "^6.2.3",
14 | "eslint-plugin-react": "7.12.4",
15 | "eslint-plugin-react-hooks": "^2.0.1"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/server/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "node": true,
4 | "commonjs": true,
5 | "es6": true,
6 | "mocha": true
7 | },
8 | "extends": "eslint:recommended",
9 | "parserOptions": {
10 | "sourceType": "module",
11 | "ecmaVersion": 2018
12 | },
13 | "rules": {
14 | "indent": [
15 | "error",
16 | "tab",
17 | { "SwitchCase": 1 }
18 | ],
19 | "linebreak-style": [
20 | "error",
21 | "unix"
22 | ],
23 | "quotes": [
24 | "error",
25 | "single"
26 | ],
27 | "semi": [
28 | "error",
29 | "never"
30 | ],
31 | "no-console": "error",
32 | }
33 | };
34 |
--------------------------------------------------------------------------------
/server/README.md:
--------------------------------------------------------------------------------
1 | ## Offline development
2 | 1. You should have Java Runtime Engine (JRE) version 6.x or newer for serverless-dynamodb-local to run
3 | 2. Run `npm start`
4 |
5 | ## Deployment
6 | 1. You should have AWS CLI set up with access key
7 | 2. Run `npm deploy`
8 |
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "version": "1.0.0",
4 | "main": "handler.js",
5 | "scripts": {
6 | "test": "echo \"Error: no test specified\" && exit 1",
7 | "postinstall": "sls dynamodb install",
8 | "start": "sls dynamodb start & sls offline",
9 | "deploy": "sls deploy"
10 | },
11 | "dependencies": {
12 | "apollo-server-lambda": "^2.14.2",
13 | "graphql": "^14.3.0",
14 | "graphql-subscriptions": "^1.1.0",
15 | "uuid": "^3.3.3"
16 | },
17 | "devDependencies": {
18 | "@babel/core": "^7.6.0",
19 | "@babel/preset-env": "^7.6.0",
20 | "aws-sdk": "^2.814.0",
21 | "babel-loader": "^8.0.6",
22 | "babel-plugin-source-map-support": "^2.1.1",
23 | "clean-terminal-webpack-plugin": "^2.0.5",
24 | "friendly-errors-webpack-plugin": "^1.7.0",
25 | "serverless-dynamodb-local": "^0.2.38",
26 | "serverless-offline": "^5.10.1",
27 | "serverless-webpack": "^5.3.1",
28 | "webpack": "^4.39.1",
29 | "webpack-node-externals": "^1.7.2"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/server/serverless.yml:
--------------------------------------------------------------------------------
1 | service: serverless-subscriptions
2 |
3 | plugins:
4 | - serverless-webpack
5 | - serverless-dynamodb-local
6 | - serverless-offline
7 |
8 | provider:
9 | name: aws
10 | runtime: nodejs8.10
11 | region: eu-west-1
12 | stage: ${opt:stage, 'dev'}
13 | environment:
14 | PUBLISH_ENDPOINT: !Join
15 | - ''
16 | - - 'https://'
17 | - !Ref WebsocketsApi
18 | - '.execute-api.${self:provider.region}.amazonaws.com/${self:provider.stage}/'
19 | TOPICS_TABLE: 'TOPIC_SUBSCRIBERS'
20 | EVENTS_TABLE: 'SUBSCRIPTION_EVENTS'
21 |
22 |
23 |
24 | package:
25 | individually: true
26 |
27 | custom:
28 | webpack:
29 | webpackConfig: 'webpack.config.js'
30 | includeModules:
31 | packagePath: './package.json'
32 | dynamodb:
33 | stages:
34 | - dev
35 | start:
36 | port: 8000
37 | inMemory: true
38 | heapInitial: 200m
39 | heapMax: 1g
40 | migrate: true
41 | convertEmptyValues: true
42 |
43 |
44 | functions:
45 | graphql:
46 | handler: src/graphql.handler
47 | role: role
48 | events:
49 | - http:
50 | path: graphql
51 | method: POST
52 | cors:
53 | origins:
54 | - '*'
55 | websocket:
56 | handler: src/websocket.handler
57 | role: role
58 | events:
59 | - websocket:
60 | route: $connect
61 | - websocket:
62 | route: $disconnect
63 | - websocket:
64 | route: $default
65 | publish:
66 | handler: src/publish.handler
67 | role: role
68 | events:
69 | - stream:
70 | type: dynamodb
71 | batchSize: 1
72 | startingPosition: LATEST
73 | arn: !GetAtt
74 | - subscriptionEventsTable
75 | - StreamArn
76 |
77 | resources:
78 | Resources:
79 | topicSubscribersTable:
80 | Type: AWS::DynamoDB::Table
81 | Properties:
82 | TableName: ${self:provider.environment.TOPICS_TABLE}
83 | ProvisionedThroughput:
84 | ReadCapacityUnits: 1
85 | WriteCapacityUnits: 1
86 | AttributeDefinitions:
87 | - AttributeName: topic
88 | AttributeType: S
89 | - AttributeName: connectionId
90 | AttributeType: S
91 | KeySchema:
92 | - AttributeName: topic
93 | KeyType: HASH
94 | - AttributeName: connectionId
95 | KeyType: RANGE
96 | TimeToLiveSpecification:
97 | AttributeName: 'ttl'
98 | Enabled: true
99 | GlobalSecondaryIndexes:
100 | -
101 | IndexName: reverse
102 | KeySchema:
103 | - AttributeName: connectionId
104 | KeyType: HASH
105 | - AttributeName: topic
106 | KeyType: RANGE
107 | Projection:
108 | ProjectionType: ALL
109 | ProvisionedThroughput:
110 | ReadCapacityUnits: 1
111 | WriteCapacityUnits: 1
112 |
113 | subscriptionEventsTable:
114 | Type: AWS::DynamoDB::Table
115 | Properties:
116 | TableName: ${self:provider.environment.EVENTS_TABLE}
117 | ProvisionedThroughput:
118 | ReadCapacityUnits: 1
119 | WriteCapacityUnits: 1
120 | AttributeDefinitions:
121 | - AttributeName: topic
122 | AttributeType: S
123 | - AttributeName: id
124 | AttributeType: S
125 | KeySchema:
126 | - AttributeName: topic
127 | KeyType: HASH
128 | - AttributeName: id
129 | KeyType: RANGE
130 | StreamSpecification:
131 | StreamViewType: NEW_IMAGE
132 |
133 | role:
134 | Type: 'AWS::IAM::Role'
135 | Properties:
136 | RoleName: ${self:service}-role
137 | AssumeRolePolicyDocument:
138 | Version: '2012-10-17'
139 | Statement:
140 | - Effect: Allow
141 | Principal:
142 | Service:
143 | - lambda.amazonaws.com
144 | Action: 'sts:AssumeRole'
145 | Policies:
146 | - PolicyName: logLambda
147 | PolicyDocument:
148 | Version: '2012-10-17'
149 | Statement:
150 | - Effect: Allow
151 | Action:
152 | - 'logs:CreateLogGroup'
153 | - 'logs:CreateLogStream'
154 | - 'logs:PutLogEvents'
155 | Resource: !Join
156 | - ''
157 | - - 'arn:aws:logs:${self:provider.region}:'
158 | - !Ref 'AWS::AccountId'
159 | - ':log-group:/aws/lambda/*:*:*'
160 | - PolicyName: useDynamoDb
161 | PolicyDocument:
162 | Version: '2012-10-17'
163 | Statement:
164 | - Effect: Allow
165 | Action:
166 | - 'dynamodb:GetItem'
167 | - 'dynamodb:PutItem'
168 | - 'dynamodb:BatchWriteItem'
169 | - 'dynamodb:Query'
170 | Resource:
171 | - !GetAtt subscriptionEventsTable.Arn
172 | - !GetAtt topicSubscribersTable.Arn
173 | - !Join
174 | - ''
175 | - - !GetAtt topicSubscribersTable.Arn
176 | - '/index/reverse'
177 | - PolicyName: accessDDBStream
178 | PolicyDocument:
179 | Version: '2012-10-17'
180 | Statement:
181 | - Effect: Allow
182 | Action:
183 | - dynamodb:DescribeStream
184 | - dynamodb:GetRecords
185 | - dynamodb:GetShardIterator
186 | - dynamodb:ListStreams
187 | Resource: !GetAtt subscriptionEventsTable.StreamArn
188 | - PolicyName: publishToConnectedClients
189 | PolicyDocument:
190 | Version: '2012-10-17'
191 | Statement:
192 | - Effect: Allow
193 | Action:
194 | - 'execute-api:Invoke'
195 | - 'execute-api:ManageConnections'
196 | Resource: !Join
197 | - ''
198 | - - 'arn:aws:execute-api:${self:provider.region}:'
199 | - !Ref 'AWS::AccountId'
200 | - ':'
201 | - !Ref WebsocketsApi
202 | - '/${self:provider.stage}/*'
203 |
204 |
205 |
--------------------------------------------------------------------------------
/server/src/graphql.js:
--------------------------------------------------------------------------------
1 | import { makeExecutableSchema } from 'graphql-tools'
2 | import { ApolloServer, gql } from 'apollo-server-lambda'
3 | import publish from './utils/publish'
4 | import subscribe from './utils/subscribe'
5 |
6 | const typeDefs = gql`
7 | type Query {
8 | _: String
9 | }
10 | type Mutation {
11 | sendMessage(message: String!): String
12 | }
13 | type Subscription {
14 | listenMessage: String
15 | }
16 | `
17 |
18 | const resolvers = {
19 | Mutation: {
20 | sendMessage: async (root, { message }) => {
21 | await publish('MY_TOPIC', { listenMessage: message })
22 | return message
23 | }
24 | },
25 | Subscription: {
26 | listenMessage: {
27 | subscribe: subscribe('MY_TOPIC')
28 | }
29 | }
30 | }
31 |
32 | export const schema = makeExecutableSchema({
33 | typeDefs,
34 | resolvers,
35 | })
36 |
37 | const server = new ApolloServer({ schema })
38 |
39 | export const handler = server.createHandler({
40 | cors: {
41 | origin: '*',
42 | credentials: true,
43 | }
44 | })
45 |
46 |
--------------------------------------------------------------------------------
/server/src/models/Client.js:
--------------------------------------------------------------------------------
1 | import ApiGatewayManagementApi from 'aws-sdk/clients/apigatewaymanagementapi'
2 | import client from '../utils/dynamodb'
3 |
4 | class Client {
5 | constructor(connectionId) {
6 | this.connectionId = connectionId
7 | }
8 |
9 | async get() {
10 | const { Item } = await client.get({
11 | TableName: process.env.TOPICS_TABLE,
12 | Key: {
13 | connectionId: this.connectionId,
14 | topic: 'INITIAL_CONNECTION'
15 | }
16 | }).promise()
17 | return Item
18 | }
19 |
20 | async getTopics() {
21 | const { Items: topics } = await client.query({
22 | ExpressionAttributeValues: {
23 | ':connectionId': this.connectionId
24 | },
25 | IndexName: 'reverse',
26 | KeyConditionExpression: 'connectionId = :connectionId',
27 | ProjectionExpression: 'topic, connectionId',
28 | TableName: process.env.TOPICS_TABLE
29 | }).promise()
30 | return topics
31 | }
32 |
33 | async removeTopics(RequestItems) {
34 | const res = await client.batchWrite({
35 | RequestItems
36 | }).promise()
37 | if(res.UnprocessedItems && res.UnprocessedItems.length) {
38 | return this.removeTopics(res.UnprocessedItems)
39 | }
40 | }
41 |
42 | async unsubscribe() {
43 | const topics = await this.getTopics()
44 | return this.removeTopics({
45 | [process.env.TOPICS_TABLE]: topics.map(({ topic, connectionId }) => ({
46 | DeleteRequest: { Key: { topic, connectionId } }
47 | }))
48 | })
49 | }
50 |
51 | async sendMessage(message) {
52 | const gatewayClient = new ApiGatewayManagementApi({
53 | apiVersion: '2018-11-29',
54 | endpoint: process.env.IS_OFFLINE ? 'http://localhost:3001' : process.env.PUBLISH_ENDPOINT
55 | })
56 | return gatewayClient.postToConnection({
57 | ConnectionId: this.connectionId,
58 | Data: JSON.stringify(message)
59 | }).promise()
60 | }
61 |
62 | async subscribe({ topic, subscriptionId, ttl }) {
63 | return client.put({
64 | Item: {
65 | topic,
66 | subscriptionId,
67 | connectionId: this.connectionId,
68 | ttl: typeof ttl === 'number' ? ttl : Math.floor(Date.now() / 1000) + 60 * 60 * 2,
69 | },
70 | TableName: process.env.TOPICS_TABLE
71 | }).promise()
72 | }
73 |
74 | async connect() {
75 | return this.subscribe({
76 | topic: 'INITIAL_CONNECTION'
77 | })
78 | }
79 | }
80 |
81 | export default Client
82 |
--------------------------------------------------------------------------------
/server/src/models/Topic.js:
--------------------------------------------------------------------------------
1 | import uuid from 'uuid'
2 | import client from '../utils/dynamodb'
3 | import { handler as publish } from '../publish'
4 | import Client from './Client'
5 |
6 | class Topic {
7 | constructor(topic) {
8 | this.topic = topic
9 | }
10 |
11 | async getSubscribers() {
12 | const { Items: clients } = await client.query({
13 | ExpressionAttributeValues: {
14 | ':topic': this.topic
15 | },
16 | KeyConditionExpression: 'topic = :topic',
17 | ProjectionExpression: 'connectionId, subscriptionId',
18 | TableName: process.env.TOPICS_TABLE
19 | }).promise()
20 | return clients
21 | }
22 |
23 | async publishMessage(data) {
24 | const subscribers = await this.getSubscribers()
25 | const promises = subscribers.map(async ({ connectionId, subscriptionId }) => {
26 | const TopicSubscriber = new Client(connectionId)
27 | try {
28 | const res = await TopicSubscriber.sendMessage({
29 | id: subscriptionId,
30 | payload: { data },
31 | type: 'data'
32 | })
33 | return res
34 | } catch(err) {
35 | if(err.statusCode === 410) { // this client has disconnected unsubscribe it
36 | return TopicSubscriber.unsubscribe()
37 | }
38 | }
39 | })
40 | return Promise.all(promises)
41 | }
42 |
43 | async postMessage(data) {
44 | const payload = {
45 | data,
46 | topic: this.topic,
47 | id: uuid.v4(),
48 | }
49 | if(process.env.IS_OFFLINE) { // dynamodb streams are not working offline so invoke lambda directly
50 | await publish({
51 | Records: [{
52 | eventName: 'INSERT',
53 | dynamodb: {
54 | NewImage: payload
55 | }
56 | }]
57 | })
58 | }
59 | return client.put({
60 | Item: payload,
61 | TableName: process.env.EVENTS_TABLE
62 | }).promise()
63 | }
64 | }
65 |
66 | export default Topic
67 |
--------------------------------------------------------------------------------
/server/src/publish.js:
--------------------------------------------------------------------------------
1 | import DynamoDB from 'aws-sdk/clients/dynamodb'
2 | import Topic from './models/Topic'
3 | const parseNewEvent = DynamoDB.Converter.unmarshall
4 |
5 | export async function handler(event) {
6 | const subscruptionEvent = event.Records[0]
7 | if(subscruptionEvent.eventName !== 'INSERT') {
8 | throw new Error('Invalid event. Wrong dynamodb event type, can publish only `INSERT` events to subscribers.')
9 | }
10 | const { topic, data } = process.env.IS_OFFLINE ?
11 | subscruptionEvent.dynamodb.NewImage :
12 | parseNewEvent(subscruptionEvent.dynamodb.NewImage)
13 | return new Topic(topic).publishMessage(data)
14 | }
15 |
--------------------------------------------------------------------------------
/server/src/utils/dynamodb.js:
--------------------------------------------------------------------------------
1 | import DynamoDB from 'aws-sdk/clients/dynamodb'
2 |
3 | const localConfig = {
4 | region: 'localhost',
5 | endpoint: 'http://localhost:8000',
6 | accessKeyId: 'DEFAULT_ACCESS_KEY',
7 | secretAccessKey: 'DEFAULT_SECRET'
8 | }
9 |
10 | const remoteConfig = {
11 | region: process.env.AWS_REGION
12 | }
13 |
14 | const client = new DynamoDB.DocumentClient(process.env.IS_OFFLINE ? localConfig : remoteConfig)
15 | export default client
16 |
--------------------------------------------------------------------------------
/server/src/utils/publish.js:
--------------------------------------------------------------------------------
1 | import Topic from '../models/Topic'
2 |
3 | const publish = (topic, data) => {
4 | return new Topic(topic).postMessage(data)
5 | }
6 |
7 | export default publish
8 |
--------------------------------------------------------------------------------
/server/src/utils/subscribe.js:
--------------------------------------------------------------------------------
1 | import Client from '../models/Client'
2 | import { PubSub } from 'graphql-subscriptions'
3 |
4 | const subscribeResolver = topic => async ({ id }, args, { connectionId, ttl }) => {
5 | await new Client(connectionId).subscribe({
6 | ttl,
7 | topic,
8 | subscriptionId: id
9 | })
10 | return new PubSub().asyncIterator([topic])
11 | }
12 |
13 | export default subscribeResolver
14 |
--------------------------------------------------------------------------------
/server/src/websocket.js:
--------------------------------------------------------------------------------
1 | import { schema } from './graphql'
2 | import { parse, getOperationAST, validate, subscribe } from 'graphql'
3 | import Client from './models/Client'
4 |
5 | export async function handler(event) {
6 | if (!(event.requestContext && event.requestContext.connectionId)) {
7 | throw new Error('Invalid event. Missing `connectionId` parameter.')
8 | }
9 |
10 | const connectionId = event.requestContext.connectionId
11 | const route = event.requestContext.routeKey
12 | const Subscriber = new Client(connectionId)
13 | const response = { statusCode: 200, body: '' }
14 |
15 | if(route === '$connect') {
16 | await new Client(connectionId).connect()
17 | return response
18 | } else if(route === '$disconnect') {
19 | await new Client(connectionId).unsubscribe()
20 | return response
21 | } else {
22 | if (!event.body) {
23 | return response
24 | }
25 |
26 | let operation = JSON.parse(event.body)
27 |
28 | if(operation.type === 'connection_init') {
29 | await Subscriber.sendMessage({ type: 'connection_ack' })
30 | return response
31 | }
32 |
33 | if(operation.type === 'stop') {
34 | return response
35 | }
36 | const client = await Subscriber.get()
37 | if(!client) {
38 | throw new Error('Unknown client')
39 | }
40 |
41 | const { query: rawQuery, variables, operationName } = operation.payload
42 | const graphqlDocument = parse(rawQuery)
43 | const operationAST = getOperationAST(graphqlDocument, operation.operationName || '')
44 |
45 | if(!operationAST || operationAST.operation !== 'subscription') {
46 | await Subscriber.sendMessage({
47 | payload: { message: 'Only subscriptions are supported' },
48 | type: 'error'
49 | })
50 | return response
51 | }
52 |
53 | const validationErrors = validate(schema, graphqlDocument)
54 | if(validationErrors.length > 0) {
55 | await Subscriber.sendMessage({
56 | payload: { errors: validationErrors },
57 | type: 'error'
58 | })
59 | return response
60 | }
61 |
62 | try {
63 | await subscribe({
64 | document: graphqlDocument,
65 | schema,
66 | rootValue: operation,
67 | operationName: operationName,
68 | variableValues: variables,
69 | contextValue: {
70 | connectionId,
71 | ttl: client.ttl
72 | }
73 | })
74 | } catch(err) {
75 | await Subscriber.sendMessage({
76 | id: operation.id,
77 | payload: err,
78 | type: 'error'
79 | })
80 | }
81 | return response
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/server/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const slsw = require('serverless-webpack')
3 | const nodeExternals = require('webpack-node-externals')
4 | const CleanTerminalPlugin = require('clean-terminal-webpack-plugin')
5 | const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin')
6 |
7 | module.exports = (() => {
8 | return {
9 | entry: slsw.lib.entries,
10 | externals: [nodeExternals()],
11 | target: 'node',
12 | stats: 'minimal',
13 | mode: slsw.lib.webpack.isLocal ? 'development': 'production',
14 | optimization: {
15 | minimize: false
16 | },
17 | performance: {
18 | hints: false
19 | },
20 | resolve: {
21 | extensions: ['.js']
22 | },
23 | module: {
24 | rules: [
25 | {
26 | test: /\.js$/,
27 | exclude: /node_modules/,
28 | use: {
29 | loader: 'babel-loader',
30 | }
31 | }
32 | ]
33 | },
34 | output: {
35 | libraryTarget: 'commonjs',
36 | path: path.resolve(__dirname, '.webpack/'),
37 | filename: '[name].js',
38 | },
39 | plugins: [
40 | new CleanTerminalPlugin(),
41 | new FriendlyErrorsWebpackPlugin(),
42 | ]
43 | }
44 | })()
45 |
--------------------------------------------------------------------------------
/showtime.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlpacaGoesCrazy/serverless-graphql-subscriptions/d0218f6f6ec15492c52fd79972fe928dab89acd3/showtime.gif
--------------------------------------------------------------------------------