Browse Source

Init. impl of Webflow API layer with API Platform

Joachim M. Giæver 2 years ago
parent
commit
2be1a35bea
60 changed files with 12119 additions and 210 deletions
  1. 15 0
      .env
  2. 7 0
      .gitignore
  3. 3 0
      assets/css/app.css
  4. 14 0
      assets/js/app.js
  5. 216 0
      assets/js/regform.js
  6. 7 0
      composer.json
  7. 3564 210
      composer.lock
  8. 10 0
      config/bundles.php
  9. 7 0
      config/packages/api_platform.yaml
  10. 3 0
      config/packages/assets.yaml
  11. 4 0
      config/packages/dev/debug.yaml
  12. 19 0
      config/packages/dev/monolog.yaml
  13. 6 0
      config/packages/dev/web_profiler.yaml
  14. 18 0
      config/packages/doctrine.yaml
  15. 10 0
      config/packages/nelmio_cors.yaml
  16. 8 0
      config/packages/prod/deprecations.yaml
  17. 20 0
      config/packages/prod/doctrine.yaml
  18. 16 0
      config/packages/prod/monolog.yaml
  19. 4 0
      config/packages/prod/webpack_encore.yaml
  20. 24 0
      config/packages/security.yaml
  21. 12 0
      config/packages/test/monolog.yaml
  22. 2 0
      config/packages/test/twig.yaml
  23. 3 0
      config/packages/test/validator.yaml
  24. 6 0
      config/packages/test/web_profiler.yaml
  25. 2 0
      config/packages/test/webpack_encore.yaml
  26. 2 0
      config/packages/twig.yaml
  27. 8 0
      config/packages/validator.yaml
  28. 25 0
      config/packages/webpack_encore.yaml
  29. 7 0
      config/routes/annotations.yaml
  30. 4 0
      config/routes/api_platform.yaml
  31. 7 0
      config/routes/dev/web_profiler.yaml
  32. 3 0
      config/services.yaml
  33. 24 0
      package.json
  34. 40 0
      src/Controller/IndexController.php
  35. 62 0
      src/DataProvider/WebflowDataProvider.php
  36. 45 0
      src/DataProvider/WebflowItemDataProvider.php
  37. 0 0
      src/Entity/.gitignore
  38. 60 0
      src/Entity/AbstractEntity.php
  39. 8 0
      src/Entity/EntityInterface.php
  40. 57 0
      src/Entity/WebflowCollection.php
  41. 57 0
      src/Entity/WebflowItem.php
  42. 80 0
      src/Filter/SearchFilter.php
  43. 20 0
      src/Http/WebflowApi/AbstractWebflowApiClient.php
  44. 52 0
      src/Http/WebflowApi/AbstractWebflowApiCollection.php
  45. 90 0
      src/Http/WebflowApi/AbstractWebflowApiField.php
  46. 19 0
      src/Http/WebflowApi/WebflowApiCollection.php
  47. 11 0
      src/Http/WebflowApi/WebflowApiCollectionItem.php
  48. 74 0
      src/Http/WebflowApi/WebflowApiCollectionItems.php
  49. 20 0
      src/Http/WebflowApi/WebflowApiCollections.php
  50. 20 0
      src/Http/WebflowApi/WebflowSite.php
  51. 31 0
      src/Http/WebflowApi/WebflowSites.php
  52. 49 0
      src/Http/WebflowApiClient.php
  53. 9 0
      src/Http/WebflowApiClientInterface.php
  54. 0 0
      src/Repository/.gitignore
  55. 293 0
      symfony.lock
  56. 20 0
      templates/api/index.html.twig
  57. 15 0
      templates/base.html.twig
  58. 17 0
      templates/index/index.html.twig
  59. 74 0
      webpack.config.js
  60. 6816 0
      yarn.lock

+ 15 - 0
.env

@@ -19,3 +19,18 @@ APP_SECRET=35cc91abb62c2f3d175f44f750c08ddb
 #TRUSTED_PROXIES=127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
 #TRUSTED_HOSTS='^(localhost|example\.com)$'
 ###< symfony/framework-bundle ###
+API_TOKEN=4ae9a18cbe797a4bbddd6d2e35c8e9b39f78589dd16a55cd861c9c48f673bdfd
+API_URL=api.webflow.com
+API_VERSION=1.0.0
+
+###> nelmio/cors-bundle ###
+CORS_ALLOW_ORIGIN=^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$
+###< nelmio/cors-bundle ###
+
+###> doctrine/doctrine-bundle ###
+# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
+# For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db"
+# For a PostgreSQL database, use: "postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=11&charset=utf8"
+# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
+DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=5.7
+###< doctrine/doctrine-bundle ###

+ 7 - 0
.gitignore

@@ -8,3 +8,10 @@
 /var/
 /vendor/
 ###< symfony/framework-bundle ###
+
+###> symfony/webpack-encore-bundle ###
+/node_modules/
+/public/build/
+npm-debug.log
+yarn-error.log
+###< symfony/webpack-encore-bundle ###

+ 3 - 0
assets/css/app.css

@@ -0,0 +1,3 @@
+body {
+    background-color: lightgray;
+}

+ 14 - 0
assets/js/app.js

@@ -0,0 +1,14 @@
+/*
+ * Welcome to your app's main JavaScript file!
+ *
+ * We recommend including the built version of this JavaScript file
+ * (and its CSS file) in your base layout (base.html.twig).
+ */
+
+// any CSS you import will output into a single css file (app.css in this case)
+import '../css/app.css';
+import React from "react";
+import ReactDOM from "react-dom";
+import RegForm from "./regform";
+
+ReactDOM.render(<RegForm />, document.getElementById('regform'));

+ 216 - 0
assets/js/regform.js

@@ -0,0 +1,216 @@
+import React, {useState} from "react";
+import axios from "axios";
+import {
+    Button,
+    Container,
+    CssBaseline,
+    Dialog,
+    DialogTitle,
+    DialogContent,
+    DialogActions,
+    Grid,
+    Paper,
+    TextField,
+    Typography,
+} from '@material-ui/core';
+import {makeStyles} from '@material-ui/core/styles';
+
+const useStyles = makeStyles((theme) => ({
+    fieldset: {
+        border: 'none',
+        //borderBottom: '1px solid',
+    },
+    form: {
+        width: '100%',
+        marginTop: theme.spacing(1),
+        flexGrow: 1,
+    },
+    paper: {
+        padding: theme.spacing(2),
+        alignItems: 'center',
+        display: 'flex',
+        flexDirection: 'column',
+    }
+}));
+
+const RegForm = (props) => {
+    const classes = useStyles();
+    return (
+        <Container component="main" maxWidth="md">
+            <CssBaseline />
+            <Paper className={classes.paper}>
+                <Typography component={"h1"} variant={"h5"}>
+                    Registrer ditt event
+                </Typography>
+                <form className={classes.form} noValidate>
+                    <fieldset className={classes.fieldset}>
+                    <Grid container spacing={3}>
+                        <Grid item xs={12}>
+                            <TextField 
+                                id={"organizer"}
+                                label={"Arrangør"}
+                                name={"organizer"}
+                                variant={"outlined"}
+                                margin={"normal"}
+                                required
+                                fullWidth
+                                autoFocus
+                            />
+                        </Grid>
+                        <Grid item xs={12} sm={4}>
+                            <TextField 
+                                id={"contact_name"}
+                                label={"Kontakt person"}
+                                name={"contact_name"}
+                                variant={"outlined"}
+                                margin={"normal"}
+                                required
+                                fullWidth
+                            />
+                        </Grid>
+                        <Grid item xs={12} sm={4}>
+                            <TextField
+                                id={"contact_email"}
+                                label={"Epostadresse"}
+                                name={"contact_email"}
+                                variant={"outlined"}
+                                margin={"normal"}
+                                type={"email"}
+                                required
+                                fullWidth
+                            />
+                        </Grid>
+                        <Grid item xs={12} sm={4}>
+                            <TextField
+                                id={"contact_phone"}
+                                label={"Telefonnummer"}
+                                name={"contact_phone"}
+                                variant={"outlined"}
+                                margin={"normal"}
+                                type={"tel"}
+                                required
+                                fullWidth
+                            />
+                        </Grid>
+                    </Grid>
+                    </fieldset>
+                    <fieldset className={classes.fieldset}>
+                        <legend>Arrangement</legend>
+                        <Grid container spacing={3}>
+                            <Grid item xs={12} sm={8}>
+                                <TextField
+                                    id={"event_name"}
+                                    label={"Navn på arrangement"}
+                                    name={"event_name"}
+                                    variant={"outlined"}
+                                    margin={"normal"}
+                                    required
+                                    fullWidth
+                                />
+                            </Grid>
+                            <Grid item xs={12} sm={4}>
+                                <TextField
+                                    id={"event_capacity"}
+                                    label={"Kapasitet"}
+                                    name={"event_capacity"}
+                                    variant={"outlined"}
+                                    margin={"normal"}
+                                    type={"number"}
+                                    required
+                                    fullWidth
+                                />
+                            </Grid>
+                            <Grid item xs={12} sm={6}>
+                                <Categories cid={"5ed0f77a42f75848a95ebf03"} />
+                            </Grid>
+                            <Grid item xs={12} sm={6}>
+                            </Grid>
+                        </Grid>
+                    </fieldset>
+                </form>
+            </Paper>
+        </Container>
+    )
+}
+
+const Categories = (props) => {
+    const [error, setError] = useState(null);
+    const [isLoading, setIsLoading] = useState(true);
+    const [availableItems, setAvailableItems] = useState([]);
+
+    React.useEffect(() => {
+        axios.get("https://magy.giaever.online/tff/api/webflow_items?cid=" + props.cid)
+            .then((response) => {
+                const data = response.data['hydra:member'];
+                setAvailableItems(data);
+            })
+            .catch((error) => {
+                console.log(error);
+                setError(error);
+            })
+            .then(() => {
+                console.log("DONE");
+                setIsLoading(false);
+            });
+    }, []);
+
+    return (
+        <DialogCancelOk
+            buttonDesc={isLoading ? "Laster kategorier" : "Velg kategori"}
+            buttonDisabled={isLoading}
+            dialogTitle={"Velg kategori(er)"}
+        >
+            {isLoading ? 
+                <p>Laster...</p>
+                :
+                <ul>{availableItems.map(item => <li key={item.id}>{item.name}</li>)}</ul>
+            }
+        </DialogCancelOk>
+    )
+}
+
+const DialogCancelOk = (props) => {
+    const [open, setOpen] = useState(false);
+
+    const handleClickOpen = () => {
+        setOpen(true);
+    };
+
+    const handleClickClose = () => {
+        setOpen(false);
+    };
+
+    return (
+        <div>
+            <Button 
+                fullWidth
+                disabled={props.buttonDisabled}
+                color={"secondary"}
+                variant={"outlined"}
+                onClick={handleClickOpen}>
+                    {props.buttonDesc}
+            </Button>
+            <Dialog 
+                disableBackdropClick
+                disableEscapeKeyDown
+                open={open}
+                onClose={handleClickClose}
+            >
+                <DialogTitle>{props.dialogTitle}</DialogTitle>
+                <DialogContent>
+                    {props.children}
+                </DialogContent>
+                <DialogActions>
+                    <Button onClick={handleClickClose} color={"primary"}>
+                        Avbryt
+                    </Button>
+                    <Button onClick={handleClickClose} color={"primary"}>
+                        Ferdig
+                    </Button>
+                </DialogActions>
+            </Dialog>
+        </div>
+    )
+}
+
+export default RegForm;

+ 7 - 0
composer.json

@@ -5,10 +5,17 @@
         "php": "^7.2.5",
         "ext-ctype": "*",
         "ext-iconv": "*",
+        "api-platform/api-pack": "^1.2",
+        "doctrine/annotations": "^1.10",
         "symfony/console": "5.1.*",
+        "symfony/debug-pack": "^1.0",
         "symfony/dotenv": "5.1.*",
         "symfony/flex": "^1.3.1",
         "symfony/framework-bundle": "5.1.*",
+        "symfony/http-client": "5.1.*",
+        "symfony/maker-bundle": "^1.19",
+        "symfony/monolog-bundle": "^3.5",
+        "symfony/webpack-encore-bundle": "^1.7",
         "symfony/yaml": "5.1.*"
     },
     "require-dev": {

File diff suppressed because it is too large
+ 3564 - 210
composer.lock


+ 10 - 0
config/bundles.php

@@ -2,4 +2,14 @@
 
 return [
     Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
+    Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
+    Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
+    Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
+    Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true],
+    Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true],
+    Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
+    Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
+    Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true],
+    Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
+    ApiPlatform\Core\Bridge\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true],
 ];

+ 7 - 0
config/packages/api_platform.yaml

@@ -0,0 +1,7 @@
+api_platform:
+    mapping:
+        paths: ['%kernel.project_dir%/src/Entity']
+    patch_formats:
+        json: ['application/merge-patch+json']
+    swagger:
+        versions: [3]

+ 3 - 0
config/packages/assets.yaml

@@ -0,0 +1,3 @@
+framework:
+    assets:
+        json_manifest_path: '%kernel.project_dir%/public/build/manifest.json'

+ 4 - 0
config/packages/dev/debug.yaml

@@ -0,0 +1,4 @@
+debug:
+    # Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser.
+    # See the "server:dump" command to start a new server.
+    dump_destination: "tcp://%env(VAR_DUMPER_SERVER)%"

+ 19 - 0
config/packages/dev/monolog.yaml

@@ -0,0 +1,19 @@
+monolog:
+    handlers:
+        main:
+            type: stream
+            path: "%kernel.logs_dir%/%kernel.environment%.log"
+            level: debug
+            channels: ["!event"]
+        # uncomment to get logging in your browser
+        # you may have to allow bigger header sizes in your Web server configuration
+        #firephp:
+        #    type: firephp
+        #    level: info
+        #chromephp:
+        #    type: chromephp
+        #    level: info
+        console:
+            type: console
+            process_psr_3_messages: false
+            channels: ["!event", "!doctrine", "!console"]

+ 6 - 0
config/packages/dev/web_profiler.yaml

@@ -0,0 +1,6 @@
+web_profiler:
+    toolbar: true
+    intercept_redirects: false
+
+framework:
+    profiler: { only_exceptions: false }

+ 18 - 0
config/packages/doctrine.yaml

@@ -0,0 +1,18 @@
+doctrine:
+    dbal:
+        url: '%env(resolve:DATABASE_URL)%'
+
+        # IMPORTANT: You MUST configure your server version,
+        # either here or in the DATABASE_URL env var (see .env file)
+        #server_version: '5.7'
+    orm:
+        auto_generate_proxy_classes: true
+        naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
+        auto_mapping: true
+        mappings:
+            App:
+                is_bundle: false
+                type: annotation
+                dir: '%kernel.project_dir%/src/Entity'
+                prefix: 'App\Entity'
+                alias: App

+ 10 - 0
config/packages/nelmio_cors.yaml

@@ -0,0 +1,10 @@
+nelmio_cors:
+    defaults:
+        origin_regex: true
+        allow_origin: ['%env(CORS_ALLOW_ORIGIN)%']
+        allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE']
+        allow_headers: ['Content-Type', 'Authorization']
+        expose_headers: ['Link']
+        max_age: 3600
+    paths:
+        '^/': null

+ 8 - 0
config/packages/prod/deprecations.yaml

@@ -0,0 +1,8 @@
+# As of Symfony 5.1, deprecations are logged in the dedicated "deprecation" channel when it exists
+#monolog:
+#    channels: [deprecation]
+#    handlers:
+#        deprecation:
+#            type: stream
+#            channels: [deprecation]
+#            path: "%kernel.logs_dir%/%kernel.environment%.deprecations.log"

+ 20 - 0
config/packages/prod/doctrine.yaml

@@ -0,0 +1,20 @@
+doctrine:
+    orm:
+        auto_generate_proxy_classes: false
+        metadata_cache_driver:
+            type: pool
+            pool: doctrine.system_cache_pool
+        query_cache_driver:
+            type: pool
+            pool: doctrine.system_cache_pool
+        result_cache_driver:
+            type: pool
+            pool: doctrine.result_cache_pool
+
+framework:
+    cache:
+        pools:
+            doctrine.result_cache_pool:
+                adapter: cache.app
+            doctrine.system_cache_pool:
+                adapter: cache.system

+ 16 - 0
config/packages/prod/monolog.yaml

@@ -0,0 +1,16 @@
+monolog:
+    handlers:
+        main:
+            type: fingers_crossed
+            action_level: error
+            handler: nested
+            excluded_http_codes: [404, 405]
+            buffer_size: 50 # How many messages should be saved? Prevent memory leaks
+        nested:
+            type: stream
+            path: "%kernel.logs_dir%/%kernel.environment%.log"
+            level: debug
+        console:
+            type: console
+            process_psr_3_messages: false
+            channels: ["!event", "!doctrine"]

+ 4 - 0
config/packages/prod/webpack_encore.yaml

@@ -0,0 +1,4 @@
+#webpack_encore:
+    # Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes)
+    # Available in version 1.2
+    #cache: true

+ 24 - 0
config/packages/security.yaml

@@ -0,0 +1,24 @@
+security:
+    # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
+    providers:
+        users_in_memory: { memory: null }
+    firewalls:
+        dev:
+            pattern: ^/(_(profiler|wdt)|css|images|js)/
+            security: false
+        main:
+            anonymous: true
+            lazy: true
+            provider: users_in_memory
+
+            # activate different ways to authenticate
+            # https://symfony.com/doc/current/security.html#firewalls-authentication
+
+            # https://symfony.com/doc/current/security/impersonating_user.html
+            # switch_user: true
+
+    # Easy way to control access for large sections of your site
+    # Note: Only the *first* access control that matches will be used
+    access_control:
+        # - { path: ^/admin, roles: ROLE_ADMIN }
+        # - { path: ^/profile, roles: ROLE_USER }

+ 12 - 0
config/packages/test/monolog.yaml

@@ -0,0 +1,12 @@
+monolog:
+    handlers:
+        main:
+            type: fingers_crossed
+            action_level: error
+            handler: nested
+            excluded_http_codes: [404, 405]
+            channels: ["!event"]
+        nested:
+            type: stream
+            path: "%kernel.logs_dir%/%kernel.environment%.log"
+            level: debug

+ 2 - 0
config/packages/test/twig.yaml

@@ -0,0 +1,2 @@
+twig:
+    strict_variables: true

+ 3 - 0
config/packages/test/validator.yaml

@@ -0,0 +1,3 @@
+framework:
+    validation:
+        not_compromised_password: false

+ 6 - 0
config/packages/test/web_profiler.yaml

@@ -0,0 +1,6 @@
+web_profiler:
+    toolbar: false
+    intercept_redirects: false
+
+framework:
+    profiler: { collect: false }

+ 2 - 0
config/packages/test/webpack_encore.yaml

@@ -0,0 +1,2 @@
+#webpack_encore:
+#    strict_mode: false

+ 2 - 0
config/packages/twig.yaml

@@ -0,0 +1,2 @@
+twig:
+    default_path: '%kernel.project_dir%/templates'

+ 8 - 0
config/packages/validator.yaml

@@ -0,0 +1,8 @@
+framework:
+    validation:
+        email_validation_mode: html5
+
+        # Enables validator auto-mapping support.
+        # For instance, basic validation constraints will be inferred from Doctrine's metadata.
+        #auto_mapping:
+        #    App\Entity\: []

+ 25 - 0
config/packages/webpack_encore.yaml

@@ -0,0 +1,25 @@
+webpack_encore:
+    # The path where Encore is building the assets - i.e. Encore.setOutputPath()
+    output_path: '%kernel.project_dir%/public/build'
+    # If multiple builds are defined (as shown below), you can disable the default build:
+    # output_path: false
+
+    # if using Encore.enableIntegrityHashes() and need the crossorigin attribute (default: false, or use 'anonymous' or 'use-credentials')
+    # crossorigin: 'anonymous'
+
+    # preload all rendered script and link tags automatically via the http2 Link header
+    # preload: true
+
+    # Throw an exception if the entrypoints.json file is missing or an entry is missing from the data
+    # strict_mode: false
+
+    # if you have multiple builds:
+    # builds:
+        # pass "frontend" as the 3rg arg to the Twig functions
+        # {{ encore_entry_script_tags('entry1', null, 'frontend') }}
+
+        # frontend: '%kernel.project_dir%/public/frontend/build'
+
+    # Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes)
+    # Put in config/packages/prod/webpack_encore.yaml
+    # cache: true

+ 7 - 0
config/routes/annotations.yaml

@@ -0,0 +1,7 @@
+controllers:
+    resource: ../../src/Controller/
+    type: annotation
+
+kernel:
+    resource: ../../src/Kernel.php
+    type: annotation

+ 4 - 0
config/routes/api_platform.yaml

@@ -0,0 +1,4 @@
+api_platform:
+    resource: .
+    type: api_platform
+    prefix: /api

+ 7 - 0
config/routes/dev/web_profiler.yaml

@@ -0,0 +1,7 @@
+web_profiler_wdt:
+    resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml'
+    prefix: /_wdt
+
+web_profiler_profiler:
+    resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml'
+    prefix: /_profiler

+ 3 - 0
config/services.yaml

@@ -23,5 +23,8 @@ services:
         resource: '../src/Controller'
         tags: ['controller.service_arguments']
 
+    App\Http\WebflowApiClient:
+      arguments: ['%env(API_TOKEN)%', '%env(API_URL)%', '%env(API_VERSION)%']
+
     # add more service definitions when explicit configuration is needed
     # please note that last definitions always *replace* previous ones

+ 24 - 0
package.json

@@ -0,0 +1,24 @@
+{
+    "devDependencies": {
+        "@babel/preset-react": "^7.10.1",
+        "@symfony/webpack-encore": "^0.30.0",
+        "core-js": "^3.0.0",
+        "regenerator-runtime": "^0.13.2",
+        "webpack-notifier": "^1.6.0"
+    },
+    "license": "UNLICENSED",
+    "private": true,
+    "scripts": {
+        "dev-server": "encore dev-server",
+        "dev": "encore dev",
+        "watch": "encore dev --watch",
+        "build": "encore production --progress"
+    },
+    "dependencies": {
+        "@material-ui/core": "^4.10.1",
+        "axios": "^0.19.2",
+        "prop-types": "^15.7.2",
+        "react": "^16.13.1",
+        "react-dom": "^16.13.1"
+    }
+}

+ 40 - 0
src/Controller/IndexController.php

@@ -0,0 +1,40 @@
+<?php
+
+namespace App\Controller;
+
+use App\Http\WebflowApi\WebflowApiCollection;
+use App\Http\WebflowApi\WebflowApiCollections;
+use App\Http\WebflowApi\WebflowSites;
+use App\Http\WebflowApi\WebflowSite;
+use App\Http\WebflowApiClient;
+use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\Routing\Annotation\Route;
+
+class IndexController extends AbstractController
+{
+    /**
+     * @Route("/", name="index")
+     */
+    public function index(WebflowSites $wfsites, WebflowApiClient $wfapi)
+    {
+        dump($wfsites->getSiteByShortName("tromsup"));
+        $site = WebflowSite::byId($wfapi, '5ebabfe546c816388d66c03a');
+
+        dump($site);
+
+        dump($site->getCollections());
+        dump((new WebflowApiCollections($wfapi, $site)));
+
+        $coll = WebflowApiCollection::byId($wfapi, '5ebbafce7b593028a5f8f29a');
+
+        dump($coll);
+
+        $items = $coll->getItems();
+
+        dump($items);
+
+        return $this->render('index/index.html.twig', [
+            'controller_name' => 'IndexController',
+        ]);
+    }
+}

+ 62 - 0
src/DataProvider/WebflowDataProvider.php

@@ -0,0 +1,62 @@
+<?php
+
+namespace App\DataProvider;
+
+use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface;
+use ApiPlatform\Core\DataProvider\ContextAwareCollectionDataProviderInterface;
+use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
+use App\Entity\WebflowCollection;
+use App\Entity\WebflowItem;
+use App\Http\WebflowApi\WebflowApiCollection;
+use App\Http\WebflowApi\WebflowSite;
+use App\Http\WebflowApiClient;
+use Psr\Log\LoggerInterface;
+
+final class WebflowDataProvider implements ContextAwareCollectionDataProviderInterface, RestrictedDataProviderInterface {
+
+    private $site;
+    private $apiClient;
+    private $logger;
+
+    public function __construct(WebflowApiClient $webflowApiClient, LoggerInterface $l) {
+        $this->site = WebflowSite::byId($webflowApiClient, '5ebabfe546c816388d66c03a');
+        $this->apiClient = $webflowApiClient;
+        $this->logger= $l;
+    }
+
+    public function supports(string $resourceClass, ?string $operationName = null, array $context = []): bool
+    {
+        return in_array($resourceClass, [
+            WebflowCollection::class,
+            WebflowItem::class,
+        ]);
+    }
+
+    public function getCollection(string $resourceClass, ?string $operationName = null, array $context = []): \Generator
+    {
+        switch ($resourceClass) {
+        case WebflowCollection::class:
+            foreach($this->site->getCollections() as $col)
+                yield new WebflowCollection($col);
+            break;
+        case WebflowItem::class:
+
+            if (isset($context['filters']) && isset($context['filters']['cid'])) {
+                $col = WebflowApiCollection::byId($this->apiClient, $context['filters']['cid']);
+
+                foreach ($col->getItems() as $item)
+                    yield new WebflowItem($item);
+
+                break;
+            }
+
+            foreach ($this->site->getCollections() as $col)
+                foreach ($col->getItems() as $item)
+                    yield new WebflowItem($item);
+            break;
+
+        }
+
+        return null;
+    }
+}

+ 45 - 0
src/DataProvider/WebflowItemDataProvider.php

@@ -0,0 +1,45 @@
+<?php
+
+namespace App\DataProvider;
+
+use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
+use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
+use App\Entity\WebflowCollection;
+use App\Http\WebflowApi\WebflowSite;
+use App\Http\WebflowApi\WebflowSites;
+use App\Http\WebflowApiClient;
+use Psr\Log\LoggerInterface;
+
+final class WebflowItemDataProvider implements ItemDataProviderInterface, RestrictedDataProviderInterface {
+
+    private $site;
+    private $apiClient;
+    private $logger;
+
+    public function __construct(WebflowApiClient $webflowApiClient, LoggerInterface $loggerInterface)
+    {
+        $this->site = WebflowSite::byId($webflowApiClient, '5ebabfe546c816388d66c03a');
+        $this->logger = $loggerInterface;
+    }
+
+    public function supports(string $resourceClass, ?string $operationName = null, array $context = []): bool {
+        return in_array($resourceClass, [
+            WebflowCollection::class,
+            WebflowItem::class,
+        ]);
+    }
+
+    public function getItem(string $resourceClass, $id, ?string $operationName = null, array $context = []): ?WebflowCollection {
+
+        switch ($resourceClass) {
+        case WebflowCollection::class:
+            foreach ($this->site->getCollections() as $col)
+                if ($col->data['_id'] == $id)
+                    return new WebflowCollection($col);
+            break;
+        }
+    
+        return null;
+    }
+
+}

+ 0 - 0
src/Entity/.gitignore


+ 60 - 0
src/Entity/AbstractEntity.php

@@ -0,0 +1,60 @@
+<?php
+
+namespace App\Entity;
+
+abstract class AbstractEntity {
+    
+    private $name;
+
+    private $slug;
+
+    private $data = [];
+
+    protected function setFromData(array $data) {
+        foreach ($data as $key => $value) {
+
+            $prop = ltrim($key, '_');
+            $method = sprintf("set%s", ucfirst(
+                $prop
+            ));
+
+            if (method_exists($this, $method)) {
+                [$this, $method]($value);
+            } elseif (property_exists($this, $prop)) {
+                $this->{$prop} = $value;
+            } else {
+                if (property_exists($this, 'data')) {
+                    if (isset($this->data[$key]))
+                        continue;
+
+                    if (strpos($key, '-') !== false)
+                        continue;
+
+                    $this->data[$key] = $value;
+                }
+            }
+        }
+    }
+
+    public function getName(): ?string {
+        return $this->name;
+    }
+
+    public function setName(string $name): self {
+        $this->name = $name;
+        return $this;
+    }
+
+    public function getSlug(): ?string {
+        return $this->slug;
+    }
+
+    public function setSlug(string $slug): self {
+        $this->slug = $slug;
+        return $this;
+    }
+
+    public function getData(): ?array {
+        return $this->data;
+    }
+}

+ 8 - 0
src/Entity/EntityInterface.php

@@ -0,0 +1,8 @@
+<?php
+
+namespace App\Entity;
+
+interface EntityInterface {
+}
+
+?>

+ 57 - 0
src/Entity/WebflowCollection.php

@@ -0,0 +1,57 @@
+<?php
+
+namespace App\Entity;
+
+use ApiPlatform\Core\Annotation\ApiProperty;
+use ApiPlatform\Core\Annotation\ApiResource;
+use App\Http\WebflowApi\WebflowApiCollection;
+use DateTimeImmutable;
+
+/**
+ * @ApiResource(
+ *  collectionOperations={"get"},
+ *  itemOperations={"get"}
+ * )
+ */
+class WebflowCollection extends AbstractEntity {
+
+    /**
+     * @ApiProperty(identifier=true)
+     */
+    protected $id;
+
+    private $name;
+
+    private $slug;
+
+    private $singularName;
+
+    protected $lastUpdated;
+    protected $createdOn;
+
+    public function __construct(WebflowApiCollection $webflowApiCollection) {
+        $this->setFromData($webflowApiCollection->data);
+    }
+
+    public function getId(): ?string {
+        return $this->id;
+    }
+
+    public function getSingularName(): ?string {
+        return $this->singularName;
+    }
+
+    public function setSingularName(string $sn): self {
+        $this->singularName = $sn;
+        return $this;
+    }
+
+    public function getLastUpdated(): \DateTimeInterface {
+        return new DateTimeImmutable($this->lastUpdated);
+    }
+
+    public function getCreatedOn(): \DateTimeInterface {
+        return new DateTimeImmutable($this->lastUpdated);
+    }
+}
+

+ 57 - 0
src/Entity/WebflowItem.php

@@ -0,0 +1,57 @@
+<?php
+
+namespace App\Entity;
+
+use ApiPlatform\Core\Annotation\ApiFilter;
+use ApiPlatform\Core\Annotation\ApiProperty;
+use ApiPlatform\Core\Annotation\ApiResource;
+use App\Http\WebflowApi\WebflowApiCollectionItem;
+use App\Filter\SearchFilter;
+
+/**
+ * @ApiResource(
+ *  collectionOperations={"get"},
+ *  itemOperations={"get"}
+ * )
+ * @ApiFilter(SearchFilter::class, properties={"cid": "exact"})
+ */
+class WebflowItem extends AbstractEntity {
+
+    /**
+     * @ApiProperty(identifier=true)
+     */
+    protected $id;
+
+    private $cid;
+
+
+    protected $archived;
+
+    protected $draft;
+
+    public function __construct(WebflowApiCollectionItem $webflowApiCollectionItem) {
+        $this->setFromData($webflowApiCollectionItem->data);
+    }
+
+    public function getId(): ?string {
+        return $this->id;
+    }
+
+    public function getCid(): ?string {
+        return $this->cid;
+    }
+
+    public function setCid(string $cid): self {
+        $this->cid = $cid;
+        return $this;
+    }
+
+    public function getArchived(): bool {
+        return $this->archived;
+    }
+
+    public function getDraft(): bool {
+        return $this->draft;
+    }
+
+}

+ 80 - 0
src/Filter/SearchFilter.php

@@ -0,0 +1,80 @@
+<?php
+
+namespace App\Filter;
+
+use ApiPlatform\Core\Api\FilterInterface;
+
+/**
+ * Class SearchFilter
+ * @package App\Filter
+ */
+class SearchFilter implements FilterInterface
+{
+    /**
+     * @var string Exact matching
+     */
+    const STRATEGY_EXACT = 'exact';
+
+    /**
+     * @var string The value must be contained in the field
+     */
+    const STRATEGY_PARTIAL = 'partial';
+
+    /**
+     * @var string Finds fields that are starting with the value
+     */
+    const STRATEGY_START = 'start';
+
+    /**
+     * @var string Finds fields that are ending with the value
+     */
+    const STRATEGY_END = 'end';
+
+    /**
+     * @var string Finds fields that are starting with the word
+     */
+    const STRATEGY_WORD_START = 'word_start';
+
+    protected $properties;
+
+    /**
+     * SearchFilter constructor.
+     * @param array|null $properties
+     */
+    public function __construct(array $properties = null)
+    {
+        $this->properties = $properties;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDescription(string $resourceClass): array
+    {
+        $description = [];
+
+        $properties = $this->properties;
+
+        foreach ($properties as $property => $strategy) {
+
+                $filterParameterNames = [
+                    $property,
+                    $property.'[]',
+                ];
+
+                foreach ($filterParameterNames as $filterParameterName) {
+                    $description[$filterParameterName] = [
+                        'property' => $property,
+                        'type' => 'string',
+                        'required' => false,
+                        'strategy' => self::STRATEGY_EXACT,
+                        'is_collection' => '[]' === substr($filterParameterName, -2),
+                    ];
+                }
+            }
+
+        return $description;
+    }
+
+}
+?>

+ 20 - 0
src/Http/WebflowApi/AbstractWebflowApiClient.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace App\Http\WebflowApi;
+
+use App\Http\WebflowApiClient;
+
+abstract class AbstractWebflowApiClient {
+
+    private $client;
+
+    public function __construct(WebflowApiClient $wfapi) {
+        $this->client = $wfapi;
+    }
+
+    protected function getClient(): WebflowApiClient {
+        return $this->client;
+    }
+
+}
+

+ 52 - 0
src/Http/WebflowApi/AbstractWebflowApiCollection.php

@@ -0,0 +1,52 @@
+<?php
+
+namespace App\Http\WebflowApi;
+
+use App\Http\WebflowApiClient;
+use App\Http\WebflowApiClientInterface;
+
+abstract class AbstractWebflowApiCollection extends AbstractWebflowApiClient {
+
+    private $data = null;
+    protected $parent = null;
+
+    public function __construct(WebflowApiClient $wpapi, ?AbstractWebflowApiClient $parent = null) {
+        parent::__construct($wpapi);
+        $this->parent = $parent;
+    }
+
+    abstract protected function getLoadScope(): string;
+
+    protected function getData(): array {
+        return $this->data ?? [];
+    } 
+
+    protected function getEntries(string $of): array {
+        if ($this->data != null)
+            return $this->data;
+
+        $req = $this->getClient()->get($this->getLoadScope());
+
+        if ($req->getStatusCode() != 200)
+            return [];
+
+        $this->data = [];
+
+        foreach ($req->toArray() as $key => $entry) {
+            if (!is_null($entry = $this->createEntry($key, $entry)))
+                $this->addEntry($entry);
+        }
+
+        return $this->data;
+
+    }
+
+    public function addEntry(AbstractWebflowApiField $f): self {
+        $this->data[] = $f;
+        return $this;
+    }
+
+    abstract public function createEntry($key, $value): ?AbstractWebflowApiField;
+}
+
+?>

+ 90 - 0
src/Http/WebflowApi/AbstractWebflowApiField.php

@@ -0,0 +1,90 @@
+<?php
+
+namespace App\Http\WebflowApi;
+
+use App\Http\WebflowApiClient;
+use RuntimeException;
+
+abstract class AbstractWebflowApiField extends AbstractWebflowApiClient {
+
+    public $data;
+    private $initialized = false;
+
+    private $collections = [];
+
+    public function __construct(WebflowApiClient $wfapi, ?array $data)
+    {
+        parent::__construct($wfapi);
+        $this->data = $data;
+        $this->initialized = !is_null($data);
+    }
+
+    public static function byId(WebflowApiClient $wfapi, string $id): AbstractWebflowApiField {
+        $new = new static($wfapi, null);
+        $new->data['_id'] ??= $id;
+        return $new;
+    }
+
+    public function isInitialized(): bool {
+        return $this->initialized;
+    }
+
+    public function load(): self {
+        if ($this->initialized)
+            return $this;
+
+        $req = $this->getClient()->get($this->getLoadScope());
+
+        if ($req->getStatusCode() == 200) {
+            $this->initialized = true;
+            $this->data = $req->toArray();
+        }
+
+        return $this;
+    }
+
+    abstract protected function getLoadScope(): string;
+
+    protected function loadCollection(string $scope, string $as): ?AbstractWebflowApiCollection {
+
+        if (isset($this->collection[$scope]))
+            return $this->collections[$scope];
+
+        if (!class_exists($as))
+            throw new RuntimeException(sprintf("Collection-class %s does not exist.", $as));
+
+        $entClass = rtrim($as, 's');
+
+        if (!class_exists($entClass))
+            throw new RuntimeException(sprintf("Entry-class %s does not exist.", $entClass));
+
+        $req = $this->getClient()->get($scope);
+
+        if ($req->getStatusCode() != 200)
+            return null;
+
+        $col = $this->collections[$scope] = new $as($this->getClient(), $this);
+
+        foreach ($req->toArray() as $key => $entry) {
+            if (isset(class_implements(get_class($col))[WebflowPaginationInterface::class])) {
+                switch ($key) {
+                case 'items': 
+                    foreach ($entry as $item)
+                        $col->addEntry($col->createEntry($key, $item));
+                    break;
+                default:
+                    $method = sprintf('set%s', ucfirst($key));
+                    if (method_exists($col, $method))
+                        [$col, $method]($entry);
+                }
+            } else {
+            if (!is_null($entry = $col->createEntry($key, $entry)))
+                $col->addEntry($entry);
+            }
+        }
+
+        return $col;
+
+    }
+}
+?>

+ 19 - 0
src/Http/WebflowApi/WebflowApiCollection.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace App\Http\WebflowApi;
+
+class WebflowApiCollection extends AbstractWebflowApiField {
+
+    protected function getLoadScope(): string
+    {
+        return sprintf('collections/%s', $this->data['_id']);
+    }
+
+    public function getItems(): ?WebflowApiCollectionItems {
+        return $this->loadCollection(
+            sprintf('collections/%s/items', $this->data['_id']),
+            WebflowApiCollectionItems::class
+        );
+    }
+
+}

+ 11 - 0
src/Http/WebflowApi/WebflowApiCollectionItem.php

@@ -0,0 +1,11 @@
+<?php
+
+namespace App\Http\WebflowApi;
+
+class WebflowApiCollectionItem extends AbstractWebflowApiField {
+
+    protected function getLoadScope(): string
+    {
+        return sprintf('collection/%s/items/%s', "", "");
+    }
+}

+ 74 - 0
src/Http/WebflowApi/WebflowApiCollectionItems.php

@@ -0,0 +1,74 @@
+<?php
+
+namespace App\Http\WebflowApi;
+
+interface WebflowPaginationInterface {
+    public function getCount(): int;
+    public function getLimit(): int;
+    public function getOffset(): int;
+    public function getTotal(): int;
+    public function next();
+}
+
+class WebflowApiCollectionItems extends AbstractWebflowApiCollection implements WebflowPaginationInterface, \IteratorAggregate {
+
+    private $count = 0;
+    private $limit = 0;
+    private $offset = 0;
+    private $total = 0;
+
+    protected function getLoadScope(): string
+    {
+        return sprintf("collections/%s/items", $this->parent->id['_id']);
+    }
+
+    public function createEntry($key, $value): ?AbstractWebflowApiField
+    {
+        return new WebflowApiCollectionItem($this->getClient(), $value);
+    }
+
+    public function getIterator(): \Traversable {
+        return new \ArrayIterator($this->getData());
+    }
+
+    public function getCount(): int {
+        return $this->count;
+    }
+
+    public function setCount(int $c): self {
+        $this->count = $c;
+        return $this;
+    }
+
+    public function getLimit(): int {
+        return $this->limit;
+    }
+
+    public function setLimit(int $l): self {
+        $this->limit = $l;
+        return $this;
+    }
+
+    public function getOffset(): int {
+        return $this->offset;
+    }
+
+    public function setOffset(int $o): self {
+        $this->offset = $o;
+        return $this;
+    }
+
+    public function getTotal(): int {
+        return $this->total;
+    }
+
+    public function setTotal(int $i): self {
+        $this->total = $i;
+        return $this;
+    }
+
+    public function next() {
+    
+    }
+
+}

+ 20 - 0
src/Http/WebflowApi/WebflowApiCollections.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace App\Http\WebflowApi;
+
+class WebflowApiCollections extends AbstractWebflowApiCollection implements \IteratorAggregate{
+
+    protected function getLoadScope(): string {
+        return sprintf('/sites/%s/collections', $this->parent->data['_id']);
+    }
+
+    public function createEntry($key, $value): ?AbstractWebflowApiField {
+       return new WebflowApiCollection($this->getClient(), $value); 
+    }
+
+    public function getIterator(): \Traversable
+    {
+        return new \ArrayIterator($this->getData());
+    }
+
+}

+ 20 - 0
src/Http/WebflowApi/WebflowSite.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace App\Http\WebflowApi;
+
+class WebflowSite extends AbstractWebflowApiField {
+
+    protected function getLoadScope(): string
+    {
+        return sprintf('sites/%s', $this->data['_id']);
+    }
+
+    public function getCollections(): ?WebflowApiCollections {
+        return $this->loadCollection(
+            sprintf('sites/%s/collections', $this->data['_id']),
+            WebflowApiCollections::class,
+        );
+    }
+}
+
+?>

+ 31 - 0
src/Http/WebflowApi/WebflowSites.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace App\Http\WebflowApi;
+
+
+class WebflowSites extends AbstractWebflowApiCollection {
+
+    public function getLoadScope(): string {
+        return '/sites';
+    }
+
+    public function getSites(): array {
+        return $this->getEntries(WebflowSite::class);
+    }
+
+    public function getSiteByShortName(string $sn): ?WebflowSite {
+
+        foreach ($this->getSites() as $site)
+            if (isset($site->data['shortName']) && $site->data['shortName'] == $sn)
+                return $site;
+
+        return null;
+    }
+
+    public function createEntry($key, $value): ?AbstractWebflowApiField
+    {
+        return new WebflowSite($this->getClient(), $value);
+    }
+}
+
+?>

+ 49 - 0
src/Http/WebflowApiClient.php

@@ -0,0 +1,49 @@
+<?php
+
+namespace App\Http;
+
+use Symfony\Component\HttpClient\HttpClient;
+use Symfony\Contracts\HttpClient\HttpClientInterface;
+use Symfony\Contracts\HttpClient\ResponseInterface;
+
+class WebflowApiClient implements WebflowApiClientInterface {
+
+    private $client;
+    private $url;
+    private $options = [];
+
+    public function __construct(string $token, string $api_url, string $version = '1.0.0') {
+
+        $this->url = $api_url;
+
+        $this->options = [
+            'auth_bearer' => $token,
+            'headers' => [
+                'accept-version' => $version,
+            ],
+        ];
+
+        $this->client = HttpClient::createForBaseUri(
+            sprintf('https://%s', $api_url), 
+            $this->options
+        );
+    }
+
+
+    public function scopeFromBase(string $scope): string {
+        return sprintf('https://%s/%s', $this->url, ltrim($scope, '/'));
+    }
+
+    public function get(string $scope): ResponseInterface {
+        return $this->client->request('GET',
+            $this->scopeFromBase($scope),
+        );
+    }
+
+    public function getClient(): HttpClientInterface {
+        return $this->client;
+    }
+
+}
+
+?>

+ 9 - 0
src/Http/WebflowApiClientInterface.php

@@ -0,0 +1,9 @@
+<?php
+
+namespace App\Http;
+
+interface WebflowApiClientInterface {
+    public function getClient();
+}
+
+?>

+ 0 - 0
src/Repository/.gitignore


+ 293 - 0
symfony.lock

@@ -1,7 +1,120 @@
 {
+    "api-platform/api-pack": {
+        "version": "v1.2.2"
+    },
+    "api-platform/core": {
+        "version": "2.5",
+        "recipe": {
+            "repo": "github.com/symfony/recipes",
+            "branch": "master",
+            "version": "2.5",
+            "ref": "a93061567140e386f107be75340ac2aee3f86cbf"
+        },
+        "files": [
+            "config/packages/api_platform.yaml",
+            "config/routes/api_platform.yaml",
+            "src/Entity/.gitignore"
+        ]
+    },
+    "doctrine/annotations": {
+        "version": "1.0",
+        "recipe": {
+            "repo": "github.com/symfony/recipes",
+            "branch": "master",
+            "version": "1.0",
+            "ref": "a2759dd6123694c8d901d0ec80006e044c2e6457"
+        },
+        "files": [
+            "config/routes/annotations.yaml"
+        ]
+    },
+    "doctrine/cache": {
+        "version": "1.10.1"
+    },
+    "doctrine/collections": {
+        "version": "1.6.5"
+    },
+    "doctrine/common": {
+        "version": "2.13.2"
+    },
+    "doctrine/dbal": {
+        "version": "2.10.2"
+    },
+    "doctrine/doctrine-bundle": {
+        "version": "2.0",
+        "recipe": {
+            "repo": "github.com/symfony/recipes",
+            "branch": "master",
+            "version": "2.0",
+            "ref": "a9f2463b9f73efe74482f831f03a204a41328555"
+        },
+        "files": [
+            "config/packages/doctrine.yaml",
+            "config/packages/prod/doctrine.yaml",
+            "src/Entity/.gitignore",
+            "src/Repository/.gitignore"
+        ]
+    },
+    "doctrine/event-manager": {
+        "version": "1.1.0"
+    },
+    "doctrine/inflector": {
+        "version": "1.4.3"
+    },
+    "doctrine/instantiator": {
+        "version": "1.3.1"
+    },
+    "doctrine/lexer": {
+        "version": "1.2.1"
+    },
+    "doctrine/orm": {
+        "version": "v2.7.3"
+    },
+    "doctrine/persistence": {
+        "version": "1.3.7"
+    },
+    "doctrine/reflection": {
+        "version": "1.2.1"
+    },
+    "doctrine/sql-formatter": {
+        "version": "1.1.0"
+    },
+    "fig/link-util": {
+        "version": "1.1.1"
+    },
+    "monolog/monolog": {
+        "version": "2.1.0"
+    },
+    "nelmio/cors-bundle": {
+        "version": "1.5",
+        "recipe": {
+            "repo": "github.com/symfony/recipes",
+            "branch": "master",
+            "version": "1.5",
+            "ref": "6388de23860284db9acce0a7a5d9d13153bcb571"
+        },
+        "files": [
+            "config/packages/nelmio_cors.yaml"
+        ]
+    },
+    "nikic/php-parser": {
+        "version": "v4.4.0"
+    },
+    "ocramius/package-versions": {
+        "version": "1.8.0"
+    },
     "php": {
         "version": "7.4"
     },
+    "phpdocumentor/reflection-common": {
+        "version": "2.1.0"
+    },
+    "phpdocumentor/reflection-docblock": {
+        "version": "5.1.0"
+    },
+    "phpdocumentor/type-resolver": {
+        "version": "1.1.0"
+    },
     "psr/cache": {
         "version": "1.0.1"
     },
@@ -11,9 +124,15 @@
     "psr/event-dispatcher": {
         "version": "1.0.0"
     },
+    "psr/link": {
+        "version": "1.0.0"
+    },
     "psr/log": {
         "version": "1.1.3"
     },
+    "symfony/asset": {
+        "version": "v5.1.0"
+    },
     "symfony/cache": {
         "version": "v5.1.0"
     },
@@ -35,12 +154,30 @@
             "bin/console"
         ]
     },
+    "symfony/debug-bundle": {
+        "version": "4.1",
+        "recipe": {
+            "repo": "github.com/symfony/recipes",
+            "branch": "master",
+            "version": "4.1",
+            "ref": "f8863cbad2f2e58c4b65fa1eac892ab189971bea"
+        },
+        "files": [
+            "config/packages/dev/debug.yaml"
+        ]
+    },
+    "symfony/debug-pack": {
+        "version": "v1.0.8"
+    },
     "symfony/dependency-injection": {
         "version": "v5.1.0"
     },
     "symfony/deprecation-contracts": {
         "version": "v2.1.2"
     },
+    "symfony/doctrine-bridge": {
+        "version": "v5.1.0"
+    },
     "symfony/dotenv": {
         "version": "v5.1.0"
     },
@@ -53,6 +190,9 @@
     "symfony/event-dispatcher-contracts": {
         "version": "v2.1.2"
     },
+    "symfony/expression-language": {
+        "version": "v5.1.0"
+    },
     "symfony/filesystem": {
         "version": "v5.1.0"
     },
@@ -90,12 +230,48 @@
             "src/Kernel.php"
         ]
     },
+    "symfony/http-client": {
+        "version": "v5.1.0"
+    },
+    "symfony/http-client-contracts": {
+        "version": "v2.1.2"
+    },
     "symfony/http-foundation": {
         "version": "v5.1.0"
     },
     "symfony/http-kernel": {
         "version": "v5.1.0"
     },
+    "symfony/inflector": {
+        "version": "v5.1.0"
+    },
+    "symfony/maker-bundle": {
+        "version": "1.0",
+        "recipe": {
+            "repo": "github.com/symfony/recipes",
+            "branch": "master",
+            "version": "1.0",
+            "ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f"
+        }
+    },
+    "symfony/monolog-bridge": {
+        "version": "v5.1.0"
+    },
+    "symfony/monolog-bundle": {
+        "version": "3.3",
+        "recipe": {
+            "repo": "github.com/symfony/recipes",
+            "branch": "master",
+            "version": "3.3",
+            "ref": "d7249f7d560f6736115eee1851d02a65826f0a56"
+        },
+        "files": [
+            "config/packages/dev/monolog.yaml",
+            "config/packages/prod/deprecations.yaml",
+            "config/packages/prod/monolog.yaml",
+            "config/packages/test/monolog.yaml"
+        ]
+    },
     "symfony/polyfill-intl-grapheme": {
         "version": "v1.17.0"
     },
@@ -111,6 +287,15 @@
     "symfony/polyfill-php80": {
         "version": "v1.17.0"
     },
+    "symfony/profiler-pack": {
+        "version": "v1.0.4"
+    },
+    "symfony/property-access": {
+        "version": "v5.1.0"
+    },
+    "symfony/property-info": {
+        "version": "v5.1.0"
+    },
     "symfony/routing": {
         "version": "5.1",
         "recipe": {
@@ -125,19 +310,127 @@
             "config/routes.yaml"
         ]
     },
+    "symfony/security-bundle": {
+        "version": "5.1",
+        "recipe": {
+            "repo": "github.com/symfony/recipes",
+            "branch": "master",
+            "version": "5.1",
+            "ref": "0a4bae19389d3b9cba1ca0102e3b2bccea724603"
+        },
+        "files": [
+            "config/packages/security.yaml"
+        ]
+    },
+    "symfony/security-core": {
+        "version": "v5.1.0"
+    },
+    "symfony/security-csrf": {
+        "version": "v5.1.0"
+    },
+    "symfony/security-guard": {
+        "version": "v5.1.0"
+    },
+    "symfony/security-http": {
+        "version": "v5.1.0"
+    },
+    "symfony/serializer": {
+        "version": "v5.1.0"
+    },
     "symfony/service-contracts": {
         "version": "v2.1.2"
     },
+    "symfony/stopwatch": {
+        "version": "v5.1.0"
+    },
     "symfony/string": {
         "version": "v5.1.0"
     },
+    "symfony/translation-contracts": {
+        "version": "v2.1.2"
+    },
+    "symfony/twig-bridge": {
+        "version": "v5.1.0"
+    },
+    "symfony/twig-bundle": {
+        "version": "5.0",
+        "recipe": {
+            "repo": "github.com/symfony/recipes",
+            "branch": "master",
+            "version": "5.0",
+            "ref": "fab9149bbaa4d5eca054ed93f9e1b66cc500895d"
+        },
+        "files": [
+            "config/packages/test/twig.yaml",
+            "config/packages/twig.yaml",
+            "templates/base.html.twig"
+        ]
+    },
+    "symfony/validator": {
+        "version": "4.3",
+        "recipe": {
+            "repo": "github.com/symfony/recipes",
+            "branch": "master",
+            "version": "4.3",
+            "ref": "d902da3e4952f18d3bf05aab29512eb61cabd869"
+        },
+        "files": [
+            "config/packages/test/validator.yaml",
+            "config/packages/validator.yaml"
+        ]
+    },
     "symfony/var-dumper": {
         "version": "v5.1.0"
     },
     "symfony/var-exporter": {
         "version": "v5.1.0"
     },
+    "symfony/web-link": {
+        "version": "v5.1.0"
+    },
+    "symfony/web-profiler-bundle": {
+        "version": "3.3",
+        "recipe": {
+            "repo": "github.com/symfony/recipes",
+            "branch": "master",
+            "version": "3.3",
+            "ref": "6bdfa1a95f6b2e677ab985cd1af2eae35d62e0f6"
+        },
+        "files": [
+            "config/packages/dev/web_profiler.yaml",
+            "config/packages/test/web_profiler.yaml",
+            "config/routes/dev/web_profiler.yaml"
+        ]
+    },
+    "symfony/webpack-encore-bundle": {
+        "version": "1.6",
+        "recipe": {
+            "repo": "github.com/symfony/recipes",
+            "branch": "master",
+            "version": "1.6",
+            "ref": "69e1d805ad95964088bd510c05995e87dc391564"
+        },
+        "files": [
+            "assets/css/app.css",
+            "assets/js/app.js",
+            "config/packages/assets.yaml",
+            "config/packages/prod/webpack_encore.yaml",
+            "config/packages/test/webpack_encore.yaml",
+            "config/packages/webpack_encore.yaml",
+            "package.json",
+            "webpack.config.js"
+        ]
+    },
     "symfony/yaml": {
         "version": "v5.1.0"
+    },
+    "twig/twig": {
+        "version": "v3.0.3"
+    },
+    "webmozart/assert": {
+        "version": "1.8.0"
+    },
+    "willdurand/negotiation": {
+        "version": "v2.3.1"
     }
 }

+ 20 - 0
templates/api/index.html.twig

@@ -0,0 +1,20 @@
+{% extends 'base.html.twig' %}
+
+{% block title %}Hello ApiController!{% endblock %}
+
+{% block body %}
+<style>
+    .example-wrapper { margin: 1em auto; max-width: 800px; width: 95%; font: 18px/1.5 sans-serif; }
+    .example-wrapper code { background: #F5F5F5; padding: 2px 6px; }
+</style>
+
+<div class="example-wrapper">
+    <h1>Hello {{ controller_name }}! ✅</h1>
+
+    This friendly message is coming from:
+    <ul>
+        <li>Your controller at <code><a href="{{ '/home/giaever.online/subdomain/magy.giaever.online/www/tff/symfony/src/Controller/ApiController.php'|file_link(0) }}">src/Controller/ApiController.php</a></code></li>
+        <li>Your template at <code><a href="{{ '/home/giaever.online/subdomain/magy.giaever.online/www/tff/symfony/templates/api/index.html.twig'|file_link(0) }}">templates/api/index.html.twig</a></code></li>
+    </ul>
+</div>
+{% endblock %}

+ 15 - 0
templates/base.html.twig

@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="UTF-8">
+        {% block meta %}
+            <meta name="viewport" content="width=device-width, initial-scale=1">
+        {% endblock %}
+        <title>{% block title %}Welcome!{% endblock %}</title>
+        {% block stylesheets %}{% endblock %}
+    </head>
+    <body>
+        {% block body %}{% endblock %}
+        {% block javascripts %}{% endblock %}
+    </body>
+</html>

+ 17 - 0
templates/index/index.html.twig

@@ -0,0 +1,17 @@
+{% extends 'base.html.twig' %}
+
+{% block title %}Hello!{% endblock %}
+
+{% block stylesheets %}
+    {{parent()}}
+    {{encore_entry_link_tags('app')}}
+{% endblock %}
+
+{% block javascripts %}
+    {{parent()}}
+    {{encore_entry_script_tags('app')}}
+{% endblock %}
+
+{% block body %}
+    <div id="regform"></div>
+{% endblock %}

+ 74 - 0
webpack.config.js

@@ -0,0 +1,74 @@
+var Encore = require('@symfony/webpack-encore');
+
+// Manually configure the runtime environment if not already configured yet by the "encore" command.
+// It's useful when you use tools that rely on webpack.config.js file.
+if (!Encore.isRuntimeEnvironmentConfigured()) {
+    Encore.configureRuntimeEnvironment(process.env.NODE_ENV || 'dev');
+}
+
+Encore
+    // directory where compiled assets will be stored
+    .setOutputPath('public/build/')
+    // public path used by the web server to access the output path
+    .setPublicPath('/tff/build')
+    // only needed for CDN's or sub-directory deploy
+    .setManifestKeyPrefix('build/')
+
+    /*
+     * ENTRY CONFIG
+     *
+     * Add 1 entry for each "page" of your app
+     * (including one that's included on every page - e.g. "app")
+     *
+     * Each entry will result in one JavaScript file (e.g. app.js)
+     * and one CSS file (e.g. app.css) if your JavaScript imports CSS.
+     */
+    .addEntry('app', './assets/js/app.js')
+    //.addEntry('page1', './assets/js/page1.js')
+    //.addEntry('page2', './assets/js/page2.js')
+
+    // When enabled, Webpack "splits" your files into smaller pieces for greater optimization.
+    .splitEntryChunks()
+
+    // will require an extra script tag for runtime.js
+    // but, you probably want this, unless you're building a single-page app
+    .enableSingleRuntimeChunk()
+
+    /*
+     * FEATURE CONFIG
+     *
+     * Enable & configure other features below. For a full
+     * list of features, see:
+     * https://symfony.com/doc/current/frontend.html#adding-more-features
+     */
+    .cleanupOutputBeforeBuild()
+    .enableBuildNotifications()
+    .enableSourceMaps(!Encore.isProduction())
+    // enables hashed filenames (e.g. app.abc123.css)
+    .enableVersioning(Encore.isProduction())
+
+    // enables @babel/preset-env polyfills
+    .configureBabelPresetEnv((config) => {
+        config.useBuiltIns = 'usage';
+        config.corejs = 3;
+    })
+
+    // enables Sass/SCSS support
+    //.enableSassLoader()
+
+    // uncomment if you use TypeScript
+    //.enableTypeScriptLoader()
+
+    // uncomment to get integrity="..." attributes on your script & link tags
+    // requires WebpackEncoreBundle 1.4 or higher
+    //.enableIntegrityHashes(Encore.isProduction())
+
+    // uncomment if you're having problems with a jQuery plugin
+    //.autoProvidejQuery()
+
+    // uncomment if you use API Platform Admin (composer req api-admin)
+    .enableReactPreset()
+    //.addEntry('admin', './assets/js/admin.js')
+;
+
+module.exports = Encore.getWebpackConfig();

+ 6816 - 0
yarn.lock

@@ -0,0 +1,6816 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@babel/code-frame@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.1.tgz#d5481c5095daa1c57e16e54c6f9198443afb49ff"
+  integrity sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw==
+  dependencies:
+    "@babel/highlight" "^7.10.1"
+
+"@babel/compat-data@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.10.1.tgz#b1085ffe72cd17bf2c0ee790fc09f9626011b2db"
+  integrity sha512-CHvCj7So7iCkGKPRFUfryXIkU2gSBw7VSZFYLsqVhrS47269VK2Hfi9S/YcublPMW8k1u2bQBlbDruoQEm4fgw==
+  dependencies:
+    browserslist "^4.12.0"
+    invariant "^2.2.4"
+    semver "^5.5.0"
+
+"@babel/core@^7.7.0":
+  version "7.10.2"
+  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.10.2.tgz#bd6786046668a925ac2bd2fd95b579b92a23b36a"
+  integrity sha512-KQmV9yguEjQsXqyOUGKjS4+3K8/DlOCE2pZcq4augdQmtTy5iv5EHtmMSJ7V4c1BIPjuwtZYqYLCq9Ga+hGBRQ==
+  dependencies:
+    "@babel/code-frame" "^7.10.1"
+    "@babel/generator" "^7.10.2"
+    "@babel/helper-module-transforms" "^7.10.1"
+    "@babel/helpers" "^7.10.1"
+    "@babel/parser" "^7.10.2"
+    "@babel/template" "^7.10.1"
+    "@babel/traverse" "^7.10.1"
+    "@babel/types" "^7.10.2"
+    convert-source-map "^1.7.0"
+    debug "^4.1.0"
+    gensync "^1.0.0-beta.1"
+    json5 "^2.1.2"
+    lodash "^4.17.13"
+    resolve "^1.3.2"
+    semver "^5.4.1"
+    source-map "^0.5.0"
+
+"@babel/generator@^7.10.1", "@babel/generator@^7.10.2":
+  version "7.10.2"
+  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.10.2.tgz#0fa5b5b2389db8bfdfcc3492b551ee20f5dd69a9"
+  integrity sha512-AxfBNHNu99DTMvlUPlt1h2+Hn7knPpH5ayJ8OqDWSeLld+Fi2AYBTC/IejWDM9Edcii4UzZRCsbUt0WlSDsDsA==
+  dependencies:
+    "@babel/types" "^7.10.2"
+    jsesc "^2.5.1"
+    lodash "^4.17.13"
+    source-map "^0.5.0"
+
+"@babel/helper-annotate-as-pure@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.1.tgz#f6d08acc6f70bbd59b436262553fb2e259a1a268"
+  integrity sha512-ewp3rvJEwLaHgyWGe4wQssC2vjks3E80WiUe2BpMb0KhreTjMROCbxXcEovTrbeGVdQct5VjQfrv9EgC+xMzCw==
+  dependencies:
+    "@babel/types" "^7.10.1"
+
+"@babel/helper-builder-binary-assignment-operator-visitor@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.1.tgz#0ec7d9be8174934532661f87783eb18d72290059"
+  integrity sha512-cQpVq48EkYxUU0xozpGCLla3wlkdRRqLWu1ksFMXA9CM5KQmyyRpSEsYXbao7JUkOw/tAaYKCaYyZq6HOFYtyw==
+  dependencies:
+    "@babel/helper-explode-assignable-expression" "^7.10.1"
+    "@babel/types" "^7.10.1"
+
+"@babel/helper-builder-react-jsx-experimental@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.10.1.tgz#9a7d58ad184d3ac3bafb1a452cec2bad7e4a0bc8"
+  integrity sha512-irQJ8kpQUV3JasXPSFQ+LCCtJSc5ceZrPFVj6TElR6XCHssi3jV8ch3odIrNtjJFRZZVbrOEfJMI79TPU/h1pQ==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.10.1"
+    "@babel/helper-module-imports" "^7.10.1"
+    "@babel/types" "^7.10.1"
+
+"@babel/helper-builder-react-jsx@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.10.1.tgz#a327f0cf983af5554701b1215de54a019f09b532"
+  integrity sha512-KXzzpyWhXgzjXIlJU1ZjIXzUPdej1suE6vzqgImZ/cpAsR/CC8gUcX4EWRmDfWz/cs6HOCPMBIJ3nKoXt3BFuw==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.10.1"
+    "@babel/types" "^7.10.1"
+
+"@babel/helper-compilation-targets@^7.10.2":
+  version "7.10.2"
+  resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.2.tgz#a17d9723b6e2c750299d2a14d4637c76936d8285"
+  integrity sha512-hYgOhF4To2UTB4LTaZepN/4Pl9LD4gfbJx8A34mqoluT8TLbof1mhUlYuNWTEebONa8+UlCC4X0TEXu7AOUyGA==
+  dependencies:
+    "@babel/compat-data" "^7.10.1"
+    browserslist "^4.12.0"
+    invariant "^2.2.4"
+    levenary "^1.1.1"
+    semver "^5.5.0"
+
+"@babel/helper-create-class-features-plugin@^7.10.1":
+  version "7.10.2"
+  resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.2.tgz#7474295770f217dbcf288bf7572eb213db46ee67"
+  integrity sha512-5C/QhkGFh1vqcziq1vAL6SI9ymzUp8BCYjFpvYVhWP4DlATIb3u5q3iUd35mvlyGs8fO7hckkW7i0tmH+5+bvQ==
+  dependencies:
+    "@babel/helper-function-name" "^7.10.1"
+    "@babel/helper-member-expression-to-functions" "^7.10.1"
+    "@babel/helper-optimise-call-expression" "^7.10.1"
+    "@babel/helper-plugin-utils" "^7.10.1"
+    "@babel/helper-replace-supers" "^7.10.1"
+    "@babel/helper-split-export-declaration" "^7.10.1"
+
+"@babel/helper-create-regexp-features-plugin@^7.10.1", "@babel/helper-create-regexp-features-plugin@^7.8.3":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.1.tgz#1b8feeab1594cbcfbf3ab5a3bbcabac0468efdbd"
+  integrity sha512-Rx4rHS0pVuJn5pJOqaqcZR4XSgeF9G/pO/79t+4r7380tXFJdzImFnxMU19f83wjSrmKHq6myrM10pFHTGzkUA==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.10.1"
+    "@babel/helper-regex" "^7.10.1"
+    regexpu-core "^4.7.0"
+
+"@babel/helper-define-map@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.10.1.tgz#5e69ee8308648470dd7900d159c044c10285221d"
+  integrity sha512-+5odWpX+OnvkD0Zmq7panrMuAGQBu6aPUgvMzuMGo4R+jUOvealEj2hiqI6WhxgKrTpFoFj0+VdsuA8KDxHBDg==
+  dependencies:
+    "@babel/helper-function-name" "^7.10.1"
+    "@babel/types" "^7.10.1"
+    lodash "^4.17.13"
+
+"@babel/helper-explode-assignable-expression@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.1.tgz#e9d76305ee1162ca467357ae25df94f179af2b7e"
+  integrity sha512-vcUJ3cDjLjvkKzt6rHrl767FeE7pMEYfPanq5L16GRtrXIoznc0HykNW2aEYkcnP76P0isoqJ34dDMFZwzEpJg==
+  dependencies:
+    "@babel/traverse" "^7.10.1"
+    "@babel/types" "^7.10.1"
+
+"@babel/helper-function-name@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.1.tgz#92bd63829bfc9215aca9d9defa85f56b539454f4"
+  integrity sha512-fcpumwhs3YyZ/ttd5Rz0xn0TpIwVkN7X0V38B9TWNfVF42KEkhkAAuPCQ3oXmtTRtiPJrmZ0TrfS0GKF0eMaRQ==
+  dependencies:
+    "@babel/helper-get-function-arity" "^7.10.1"
+    "@babel/template" "^7.10.1"
+    "@babel/types" "^7.10.1"
+
+"@babel/helper-get-function-arity@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.1.tgz#7303390a81ba7cb59613895a192b93850e373f7d"
+  integrity sha512-F5qdXkYGOQUb0hpRaPoetF9AnsXknKjWMZ+wmsIRsp5ge5sFh4c3h1eH2pRTTuy9KKAA2+TTYomGXAtEL2fQEw==
+  dependencies:
+    "@babel/types" "^7.10.1"
+
+"@babel/helper-hoist-variables@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.1.tgz#7e77c82e5dcae1ebf123174c385aaadbf787d077"
+  integrity sha512-vLm5srkU8rI6X3+aQ1rQJyfjvCBLXP8cAGeuw04zeAM2ItKb1e7pmVmLyHb4sDaAYnLL13RHOZPLEtcGZ5xvjg==
+  dependencies:
+    "@babel/types" "^7.10.1"
+
+"@babel/helper-member-expression-to-functions@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.1.tgz#432967fd7e12a4afef66c4687d4ca22bc0456f15"
+  integrity sha512-u7XLXeM2n50gb6PWJ9hoO5oO7JFPaZtrh35t8RqKLT1jFKj9IWeD1zrcrYp1q1qiZTdEarfDWfTIP8nGsu0h5g==
+  dependencies:
+    "@babel/types" "^7.10.1"
+
+"@babel/helper-module-imports@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.1.tgz#dd331bd45bccc566ce77004e9d05fe17add13876"
+  integrity sha512-SFxgwYmZ3HZPyZwJRiVNLRHWuW2OgE5k2nrVs6D9Iv4PPnXVffuEHy83Sfx/l4SqF+5kyJXjAyUmrG7tNm+qVg==
+  dependencies:
+    "@babel/types" "^7.10.1"
+
+"@babel/helper-module-transforms@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.10.1.tgz#24e2f08ee6832c60b157bb0936c86bef7210c622"
+  integrity sha512-RLHRCAzyJe7Q7sF4oy2cB+kRnU4wDZY/H2xJFGof+M+SJEGhZsb+GFj5j1AD8NiSaVBJ+Pf0/WObiXu/zxWpFg==
+  dependencies:
+    "@babel/helper-module-imports" "^7.10.1"
+    "@babel/helper-replace-supers" "^7.10.1"
+    "@babel/helper-simple-access" "^7.10.1"
+    "@babel/helper-split-export-declaration" "^7.10.1"
+    "@babel/template" "^7.10.1"
+    "@babel/types" "^7.10.1"
+    lodash "^4.17.13"
+
+"@babel/helper-optimise-call-expression@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.1.tgz#b4a1f2561870ce1247ceddb02a3860fa96d72543"
+  integrity sha512-a0DjNS1prnBsoKx83dP2falChcs7p3i8VMzdrSbfLhuQra/2ENC4sbri34dz/rWmDADsmF1q5GbfaXydh0Jbjg==
+  dependencies:
+    "@babel/types" "^7.10.1"
+
+"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.1", "@babel/helper-plugin-utils@^7.8.0":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.1.tgz#ec5a5cf0eec925b66c60580328b122c01230a127"
+  integrity sha512-fvoGeXt0bJc7VMWZGCAEBEMo/HAjW2mP8apF5eXK0wSqwLAVHAISCWRoLMBMUs2kqeaG77jltVqu4Hn8Egl3nA==
+
+"@babel/helper-regex@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.10.1.tgz#021cf1a7ba99822f993222a001cc3fec83255b96"
+  integrity sha512-7isHr19RsIJWWLLFn21ubFt223PjQyg1HY7CZEMRr820HttHPpVvrsIN3bUOo44DEfFV4kBXO7Abbn9KTUZV7g==
+  dependencies:
+    lodash "^4.17.13"
+
+"@babel/helper-remap-async-to-generator@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.1.tgz#bad6aaa4ff39ce8d4b82ccaae0bfe0f7dbb5f432"
+  integrity sha512-RfX1P8HqsfgmJ6CwaXGKMAqbYdlleqglvVtht0HGPMSsy2V6MqLlOJVF/0Qyb/m2ZCi2z3q3+s6Pv7R/dQuZ6A==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.10.1"
+    "@babel/helper-wrap-function" "^7.10.1"
+    "@babel/template" "^7.10.1"
+    "@babel/traverse" "^7.10.1"
+    "@babel/types" "^7.10.1"
+
+"@babel/helper-replace-supers@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.1.tgz#ec6859d20c5d8087f6a2dc4e014db7228975f13d"
+  integrity sha512-SOwJzEfpuQwInzzQJGjGaiG578UYmyi2Xw668klPWV5n07B73S0a9btjLk/52Mlcxa+5AdIYqws1KyXRfMoB7A==
+  dependencies:
+    "@babel/helper-member-expression-to-functions" "^7.10.1"
+    "@babel/helper-optimise-call-expression" "^7.10.1"
+    "@babel/traverse" "^7.10.1"
+    "@babel/types" "^7.10.1"
+
+"@babel/helper-simple-access@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.1.tgz#08fb7e22ace9eb8326f7e3920a1c2052f13d851e"
+  integrity sha512-VSWpWzRzn9VtgMJBIWTZ+GP107kZdQ4YplJlCmIrjoLVSi/0upixezHCDG8kpPVTBJpKfxTH01wDhh+jS2zKbw==
+  dependencies:
+    "@babel/template" "^7.10.1"
+    "@babel/types" "^7.10.1"
+
+"@babel/helper-split-export-declaration@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.1.tgz#c6f4be1cbc15e3a868e4c64a17d5d31d754da35f"
+  integrity sha512-UQ1LVBPrYdbchNhLwj6fetj46BcFwfS4NllJo/1aJsT+1dLTEnXJL0qHqtY7gPzF8S2fXBJamf1biAXV3X077g==
+  dependencies:
+    "@babel/types" "^7.10.1"
+
+"@babel/helper-validator-identifier@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz#5770b0c1a826c4f53f5ede5e153163e0318e94b5"
+  integrity sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw==
+
+"@babel/helper-wrap-function@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.1.tgz#956d1310d6696257a7afd47e4c42dfda5dfcedc9"
+  integrity sha512-C0MzRGteVDn+H32/ZgbAv5r56f2o1fZSA/rj/TYo8JEJNHg+9BdSmKBUND0shxWRztWhjlT2cvHYuynpPsVJwQ==
+  dependencies:
+    "@babel/helper-function-name" "^7.10.1"
+    "@babel/template" "^7.10.1"
+    "@babel/traverse" "^7.10.1"
+    "@babel/types" "^7.10.1"
+
+"@babel/helpers@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.1.tgz#a6827b7cb975c9d9cef5fd61d919f60d8844a973"
+  integrity sha512-muQNHF+IdU6wGgkaJyhhEmI54MOZBKsFfsXFhboz1ybwJ1Kl7IHlbm2a++4jwrmY5UYsgitt5lfqo1wMFcHmyw==
+  dependencies:
+    "@babel/template" "^7.10.1"
+    "@babel/traverse" "^7.10.1"
+    "@babel/types" "^7.10.1"
+
+"@babel/highlight@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.1.tgz#841d098ba613ba1a427a2b383d79e35552c38ae0"
+  integrity sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.10.1"
+    chalk "^2.0.0"
+    js-tokens "^4.0.0"
+
+"@babel/parser@^7.10.1", "@babel/parser@^7.10.2":
+  version "7.10.2"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.2.tgz#871807f10442b92ff97e4783b9b54f6a0ca812d0"
+  integrity sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ==
+
+"@babel/plugin-proposal-async-generator-functions@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.1.tgz#6911af5ba2e615c4ff3c497fe2f47b35bf6d7e55"
+  integrity sha512-vzZE12ZTdB336POZjmpblWfNNRpMSua45EYnRigE2XsZxcXcIyly2ixnTJasJE4Zq3U7t2d8rRF7XRUuzHxbOw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+    "@babel/helper-remap-async-to-generator" "^7.10.1"
+    "@babel/plugin-syntax-async-generators" "^7.8.0"
+
+"@babel/plugin-proposal-class-properties@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.1.tgz#046bc7f6550bb08d9bd1d4f060f5f5a4f1087e01"
+  integrity sha512-sqdGWgoXlnOdgMXU+9MbhzwFRgxVLeiGBqTrnuS7LC2IBU31wSsESbTUreT2O418obpfPdGUR2GbEufZF1bpqw==
+  dependencies:
+    "@babel/helper-create-class-features-plugin" "^7.10.1"
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-proposal-dynamic-import@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.1.tgz#e36979dc1dc3b73f6d6816fc4951da2363488ef0"
+  integrity sha512-Cpc2yUVHTEGPlmiQzXj026kqwjEQAD9I4ZC16uzdbgWgitg/UHKHLffKNCQZ5+y8jpIZPJcKcwsr2HwPh+w3XA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+    "@babel/plugin-syntax-dynamic-import" "^7.8.0"
+
+"@babel/plugin-proposal-json-strings@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.1.tgz#b1e691ee24c651b5a5e32213222b2379734aff09"
+  integrity sha512-m8r5BmV+ZLpWPtMY2mOKN7wre6HIO4gfIiV+eOmsnZABNenrt/kzYBwrh+KOfgumSWpnlGs5F70J8afYMSJMBg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+    "@babel/plugin-syntax-json-strings" "^7.8.0"
+
+"@babel/plugin-proposal-nullish-coalescing-operator@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.1.tgz#02dca21673842ff2fe763ac253777f235e9bbf78"
+  integrity sha512-56cI/uHYgL2C8HVuHOuvVowihhX0sxb3nnfVRzUeVHTWmRHTZrKuAh/OBIMggGU/S1g/1D2CRCXqP+3u7vX7iA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+    "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0"
+
+"@babel/plugin-proposal-numeric-separator@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.1.tgz#a9a38bc34f78bdfd981e791c27c6fdcec478c123"
+  integrity sha512-jjfym4N9HtCiNfyyLAVD8WqPYeHUrw4ihxuAynWj6zzp2gf9Ey2f7ImhFm6ikB3CLf5Z/zmcJDri6B4+9j9RsA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+    "@babel/plugin-syntax-numeric-separator" "^7.10.1"
+
+"@babel/plugin-proposal-object-rest-spread@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.10.1.tgz#cba44908ac9f142650b4a65b8aa06bf3478d5fb6"
+  integrity sha512-Z+Qri55KiQkHh7Fc4BW6o+QBuTagbOp9txE+4U1i79u9oWlf2npkiDx+Rf3iK3lbcHBuNy9UOkwuR5wOMH3LIQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+    "@babel/plugin-syntax-object-rest-spread" "^7.8.0"
+    "@babel/plugin-transform-parameters" "^7.10.1"
+
+"@babel/plugin-proposal-optional-catch-binding@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.1.tgz#c9f86d99305f9fa531b568ff5ab8c964b8b223d2"
+  integrity sha512-VqExgeE62YBqI3ogkGoOJp1R6u12DFZjqwJhqtKc2o5m1YTUuUWnos7bZQFBhwkxIFpWYJ7uB75U7VAPPiKETA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+    "@babel/plugin-syntax-optional-catch-binding" "^7.8.0"
+
+"@babel/plugin-proposal-optional-chaining@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.10.1.tgz#15f5d6d22708629451a91be28f8facc55b0e818c"
+  integrity sha512-dqQj475q8+/avvok72CF3AOSV/SGEcH29zT5hhohqqvvZ2+boQoOr7iGldBG5YXTO2qgCgc2B3WvVLUdbeMlGA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+    "@babel/plugin-syntax-optional-chaining" "^7.8.0"
+
+"@babel/plugin-proposal-private-methods@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.1.tgz#ed85e8058ab0fe309c3f448e5e1b73ca89cdb598"
+  integrity sha512-RZecFFJjDiQ2z6maFprLgrdnm0OzoC23Mx89xf1CcEsxmHuzuXOdniEuI+S3v7vjQG4F5sa6YtUp+19sZuSxHg==
+  dependencies:
+    "@babel/helper-create-class-features-plugin" "^7.10.1"
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-proposal-unicode-property-regex@^7.10.1", "@babel/plugin-proposal-unicode-property-regex@^7.4.4":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.1.tgz#dc04feb25e2dd70c12b05d680190e138fa2c0c6f"
+  integrity sha512-JjfngYRvwmPwmnbRZyNiPFI8zxCZb8euzbCG/LxyKdeTb59tVciKo9GK9bi6JYKInk1H11Dq9j/zRqIH4KigfQ==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.10.1"
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-syntax-async-generators@^7.8.0":
+  version "7.8.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d"
+  integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-class-properties@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.1.tgz#d5bc0645913df5b17ad7eda0fa2308330bde34c5"
+  integrity sha512-Gf2Yx/iRs1JREDtVZ56OrjjgFHCaldpTnuy9BHla10qyVT3YkIIGEtoDWhyop0ksu1GvNjHIoYRBqm3zoR1jyQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-syntax-dynamic-import@^7.0.0", "@babel/plugin-syntax-dynamic-import@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3"
+  integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-json-strings@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a"
+  integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-jsx@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.10.1.tgz#0ae371134a42b91d5418feb3c8c8d43e1565d2da"
+  integrity sha512-+OxyOArpVFXQeXKLO9o+r2I4dIoVoy6+Uu0vKELrlweDM3QJADZj+Z+5ERansZqIZBcLj42vHnDI8Rz9BnRIuQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9"
+  integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-numeric-separator@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.1.tgz#25761ee7410bc8cf97327ba741ee94e4a61b7d99"
+  integrity sha512-uTd0OsHrpe3tH5gRPTxG8Voh99/WCU78vIm5NMRYPAqC8lR4vajt6KkCAknCHrx24vkPdd/05yfdGSB4EIY2mg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-syntax-object-rest-spread@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871"
+  integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-optional-catch-binding@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1"
+  integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-optional-chaining@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a"
+  integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-top-level-await@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.1.tgz#8b8733f8c57397b3eaa47ddba8841586dcaef362"
+  integrity sha512-hgA5RYkmZm8FTFT3yu2N9Bx7yVVOKYT6yEdXXo6j2JTm0wNxgqaGeQVaSHRjhfnQbX91DtjFB6McRFSlcJH3xQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-transform-arrow-functions@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.1.tgz#cb5ee3a36f0863c06ead0b409b4cc43a889b295b"
+  integrity sha512-6AZHgFJKP3DJX0eCNJj01RpytUa3SOGawIxweHkNX2L6PYikOZmoh5B0d7hIHaIgveMjX990IAa/xK7jRTN8OA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-transform-async-to-generator@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.1.tgz#e5153eb1a3e028f79194ed8a7a4bf55f862b2062"
+  integrity sha512-XCgYjJ8TY2slj6SReBUyamJn3k2JLUIiiR5b6t1mNCMSvv7yx+jJpaewakikp0uWFQSF7ChPPoe3dHmXLpISkg==
+  dependencies:
+    "@babel/helper-module-imports" "^7.10.1"
+    "@babel/helper-plugin-utils" "^7.10.1"
+    "@babel/helper-remap-async-to-generator" "^7.10.1"
+
+"@babel/plugin-transform-block-scoped-functions@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.1.tgz#146856e756d54b20fff14b819456b3e01820b85d"
+  integrity sha512-B7K15Xp8lv0sOJrdVAoukKlxP9N59HS48V1J3U/JGj+Ad+MHq+am6xJVs85AgXrQn4LV8vaYFOB+pr/yIuzW8Q==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-transform-block-scoping@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.10.1.tgz#47092d89ca345811451cd0dc5d91605982705d5e"
+  integrity sha512-8bpWG6TtF5akdhIm/uWTyjHqENpy13Fx8chg7pFH875aNLwX8JxIxqm08gmAT+Whe6AOmaTeLPe7dpLbXt+xUw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+    lodash "^4.17.13"
+
+"@babel/plugin-transform-classes@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.1.tgz#6e11dd6c4dfae70f540480a4702477ed766d733f"
+  integrity sha512-P9V0YIh+ln/B3RStPoXpEQ/CoAxQIhRSUn7aXqQ+FZJ2u8+oCtjIXR3+X0vsSD8zv+mb56K7wZW1XiDTDGiDRQ==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.10.1"
+    "@babel/helper-define-map" "^7.10.1"
+    "@babel/helper-function-name" "^7.10.1"
+    "@babel/helper-optimise-call-expression" "^7.10.1"
+    "@babel/helper-plugin-utils" "^7.10.1"
+    "@babel/helper-replace-supers" "^7.10.1"
+    "@babel/helper-split-export-declaration" "^7.10.1"
+    globals "^11.1.0"
+
+"@babel/plugin-transform-computed-properties@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.1.tgz#59aa399064429d64dce5cf76ef9b90b7245ebd07"
+  integrity sha512-mqSrGjp3IefMsXIenBfGcPXxJxweQe2hEIwMQvjtiDQ9b1IBvDUjkAtV/HMXX47/vXf14qDNedXsIiNd1FmkaQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-transform-destructuring@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.1.tgz#abd58e51337815ca3a22a336b85f62b998e71907"
+  integrity sha512-V/nUc4yGWG71OhaTH705pU8ZSdM6c1KmmLP8ys59oOYbT7RpMYAR3MsVOt6OHL0WzG7BlTU076va9fjJyYzJMA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-transform-dotall-regex@^7.10.1", "@babel/plugin-transform-dotall-regex@^7.4.4":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.1.tgz#920b9fec2d78bb57ebb64a644d5c2ba67cc104ee"
+  integrity sha512-19VIMsD1dp02RvduFUmfzj8uknaO3uiHHF0s3E1OHnVsNj8oge8EQ5RzHRbJjGSetRnkEuBYO7TG1M5kKjGLOA==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.10.1"
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-transform-duplicate-keys@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.1.tgz#c900a793beb096bc9d4d0a9d0cde19518ffc83b9"
+  integrity sha512-wIEpkX4QvX8Mo9W6XF3EdGttrIPZWozHfEaDTU0WJD/TDnXMvdDh30mzUl/9qWhnf7naicYartcEfUghTCSNpA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-transform-exponentiation-operator@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.1.tgz#279c3116756a60dd6e6f5e488ba7957db9c59eb3"
+  integrity sha512-lr/przdAbpEA2BUzRvjXdEDLrArGRRPwbaF9rvayuHRvdQ7lUTTkZnhZrJ4LE2jvgMRFF4f0YuPQ20vhiPYxtA==
+  dependencies:
+    "@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.1"
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-transform-for-of@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.1.tgz#ff01119784eb0ee32258e8646157ba2501fcfda5"
+  integrity sha512-US8KCuxfQcn0LwSCMWMma8M2R5mAjJGsmoCBVwlMygvmDUMkTCykc84IqN1M7t+agSfOmLYTInLCHJM+RUoz+w==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-transform-function-name@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.1.tgz#4ed46fd6e1d8fde2a2ec7b03c66d853d2c92427d"
+  integrity sha512-//bsKsKFBJfGd65qSNNh1exBy5Y9gD9ZN+DvrJ8f7HXr4avE5POW6zB7Rj6VnqHV33+0vXWUwJT0wSHubiAQkw==
+  dependencies:
+    "@babel/helper-function-name" "^7.10.1"
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-transform-literals@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.1.tgz#5794f8da82846b22e4e6631ea1658bce708eb46a"
+  integrity sha512-qi0+5qgevz1NHLZroObRm5A+8JJtibb7vdcPQF1KQE12+Y/xxl8coJ+TpPW9iRq+Mhw/NKLjm+5SHtAHCC7lAw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-transform-member-expression-literals@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.1.tgz#90347cba31bca6f394b3f7bd95d2bbfd9fce2f39"
+  integrity sha512-UmaWhDokOFT2GcgU6MkHC11i0NQcL63iqeufXWfRy6pUOGYeCGEKhvfFO6Vz70UfYJYHwveg62GS83Rvpxn+NA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-transform-modules-amd@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.1.tgz#65950e8e05797ebd2fe532b96e19fc5482a1d52a"
+  integrity sha512-31+hnWSFRI4/ACFr1qkboBbrTxoBIzj7qA69qlq8HY8p7+YCzkCT6/TvQ1a4B0z27VeWtAeJd6pr5G04dc1iHw==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.10.1"
+    "@babel/helper-plugin-utils" "^7.10.1"
+    babel-plugin-dynamic-import-node "^2.3.3"
+
+"@babel/plugin-transform-modules-commonjs@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.1.tgz#d5ff4b4413ed97ffded99961056e1fb980fb9301"
+  integrity sha512-AQG4fc3KOah0vdITwt7Gi6hD9BtQP/8bhem7OjbaMoRNCH5Djx42O2vYMfau7QnAzQCa+RJnhJBmFFMGpQEzrg==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.10.1"
+    "@babel/helper-plugin-utils" "^7.10.1"
+    "@babel/helper-simple-access" "^7.10.1"
+    babel-plugin-dynamic-import-node "^2.3.3"
+
+"@babel/plugin-transform-modules-systemjs@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.1.tgz#9962e4b0ac6aaf2e20431ada3d8ec72082cbffb6"
+  integrity sha512-ewNKcj1TQZDL3YnO85qh9zo1YF1CHgmSTlRQgHqe63oTrMI85cthKtZjAiZSsSNjPQ5NCaYo5QkbYqEw1ZBgZA==
+  dependencies:
+    "@babel/helper-hoist-variables" "^7.10.1"
+    "@babel/helper-module-transforms" "^7.10.1"
+    "@babel/helper-plugin-utils" "^7.10.1"
+    babel-plugin-dynamic-import-node "^2.3.3"
+
+"@babel/plugin-transform-modules-umd@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.1.tgz#ea080911ffc6eb21840a5197a39ede4ee67b1595"
+  integrity sha512-EIuiRNMd6GB6ulcYlETnYYfgv4AxqrswghmBRQbWLHZxN4s7mupxzglnHqk9ZiUpDI4eRWewedJJNj67PWOXKA==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.10.1"
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-transform-named-capturing-groups-regex@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz#a2a72bffa202ac0e2d0506afd0939c5ecbc48c6c"
+  integrity sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.8.3"
+
+"@babel/plugin-transform-new-target@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.1.tgz#6ee41a5e648da7632e22b6fb54012e87f612f324"
+  integrity sha512-MBlzPc1nJvbmO9rPr1fQwXOM2iGut+JC92ku6PbiJMMK7SnQc1rytgpopveE3Evn47gzvGYeCdgfCDbZo0ecUw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-transform-object-super@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.1.tgz#2e3016b0adbf262983bf0d5121d676a5ed9c4fde"
+  integrity sha512-WnnStUDN5GL+wGQrJylrnnVlFhFmeArINIR9gjhSeYyvroGhBrSAXYg/RHsnfzmsa+onJrTJrEClPzgNmmQ4Gw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+    "@babel/helper-replace-supers" "^7.10.1"
+
+"@babel/plugin-transform-parameters@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.1.tgz#b25938a3c5fae0354144a720b07b32766f683ddd"
+  integrity sha512-tJ1T0n6g4dXMsL45YsSzzSDZCxiHXAQp/qHrucOq5gEHncTA3xDxnd5+sZcoQp+N1ZbieAaB8r/VUCG0gqseOg==
+  dependencies:
+    "@babel/helper-get-function-arity" "^7.10.1"
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-transform-property-literals@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.1.tgz#cffc7315219230ed81dc53e4625bf86815b6050d"
+  integrity sha512-Kr6+mgag8auNrgEpbfIWzdXYOvqDHZOF0+Bx2xh4H2EDNwcbRb9lY6nkZg8oSjsX+DH9Ebxm9hOqtKW+gRDeNA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-transform-react-display-name@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.10.1.tgz#e6a33f6d48dfb213dda5e007d0c7ff82b6a3d8ef"
+  integrity sha512-rBjKcVwjk26H3VX8pavMxGf33LNlbocMHdSeldIEswtQ/hrjyTG8fKKILW1cSkODyRovckN/uZlGb2+sAV9JUQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-transform-react-jsx-development@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.10.1.tgz#1ac6300d8b28ef381ee48e6fec430cc38047b7f3"
+  integrity sha512-XwDy/FFoCfw9wGFtdn5Z+dHh6HXKHkC6DwKNWpN74VWinUagZfDcEJc3Y8Dn5B3WMVnAllX8Kviaw7MtC5Epwg==
+  dependencies:
+    "@babel/helper-builder-react-jsx-experimental" "^7.10.1"
+    "@babel/helper-plugin-utils" "^7.10.1"
+    "@babel/plugin-syntax-jsx" "^7.10.1"
+
+"@babel/plugin-transform-react-jsx-self@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.10.1.tgz#22143e14388d72eb88649606bb9e46f421bc3821"
+  integrity sha512-4p+RBw9d1qV4S749J42ZooeQaBomFPrSxa9JONLHJ1TxCBo3TzJ79vtmG2S2erUT8PDDrPdw4ZbXGr2/1+dILA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+    "@babel/plugin-syntax-jsx" "^7.10.1"
+
+"@babel/plugin-transform-react-jsx-source@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.10.1.tgz#30db3d4ee3cdebbb26a82a9703673714777a4273"
+  integrity sha512-neAbaKkoiL+LXYbGDvh6PjPG+YeA67OsZlE78u50xbWh2L1/C81uHiNP5d1fw+uqUIoiNdCC8ZB+G4Zh3hShJA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+    "@babel/plugin-syntax-jsx" "^7.10.1"
+
+"@babel/plugin-transform-react-jsx@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.10.1.tgz#91f544248ba131486decb5d9806da6a6e19a2896"
+  integrity sha512-MBVworWiSRBap3Vs39eHt+6pJuLUAaK4oxGc8g+wY+vuSJvLiEQjW1LSTqKb8OUPtDvHCkdPhk7d6sjC19xyFw==
+  dependencies:
+    "@babel/helper-builder-react-jsx" "^7.10.1"
+    "@babel/helper-builder-react-jsx-experimental" "^7.10.1"
+    "@babel/helper-plugin-utils" "^7.10.1"
+    "@babel/plugin-syntax-jsx" "^7.10.1"
+
+"@babel/plugin-transform-react-pure-annotations@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.10.1.tgz#f5e7c755d3e7614d4c926e144f501648a5277b70"
+  integrity sha512-mfhoiai083AkeewsBHUpaS/FM1dmUENHBMpS/tugSJ7VXqXO5dCN1Gkint2YvM1Cdv1uhmAKt1ZOuAjceKmlLA==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.10.1"
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-transform-regenerator@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.1.tgz#10e175cbe7bdb63cc9b39f9b3f823c5c7c5c5490"
+  integrity sha512-B3+Y2prScgJ2Bh/2l9LJxKbb8C8kRfsG4AdPT+n7ixBHIxJaIG8bi8tgjxUMege1+WqSJ+7gu1YeoMVO3gPWzw==
+  dependencies:
+    regenerator-transform "^0.14.2"
+
+"@babel/plugin-transform-reserved-words@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.1.tgz#0fc1027312b4d1c3276a57890c8ae3bcc0b64a86"
+  integrity sha512-qN1OMoE2nuqSPmpTqEM7OvJ1FkMEV+BjVeZZm9V9mq/x1JLKQ4pcv8riZJMNN3u2AUGl0ouOMjRr2siecvHqUQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-transform-shorthand-properties@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.1.tgz#e8b54f238a1ccbae482c4dce946180ae7b3143f3"
+  integrity sha512-AR0E/lZMfLstScFwztApGeyTHJ5u3JUKMjneqRItWeEqDdHWZwAOKycvQNCasCK/3r5YXsuNG25funcJDu7Y2g==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-transform-spread@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.10.1.tgz#0c6d618a0c4461a274418460a28c9ccf5239a7c8"
+  integrity sha512-8wTPym6edIrClW8FI2IoaePB91ETOtg36dOkj3bYcNe7aDMN2FXEoUa+WrmPc4xa1u2PQK46fUX2aCb+zo9rfw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-transform-sticky-regex@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.1.tgz#90fc89b7526228bed9842cff3588270a7a393b00"
+  integrity sha512-j17ojftKjrL7ufX8ajKvwRilwqTok4q+BjkknmQw9VNHnItTyMP5anPFzxFJdCQs7clLcWpCV3ma+6qZWLnGMA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+    "@babel/helper-regex" "^7.10.1"
+
+"@babel/plugin-transform-template-literals@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.1.tgz#914c7b7f4752c570ea00553b4284dad8070e8628"
+  integrity sha512-t7B/3MQf5M1T9hPCRG28DNGZUuxAuDqLYS03rJrIk2prj/UV7Z6FOneijhQhnv/Xa039vidXeVbvjK2SK5f7Gg==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.10.1"
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-transform-typeof-symbol@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.1.tgz#60c0239b69965d166b80a84de7315c1bc7e0bb0e"
+  integrity sha512-qX8KZcmbvA23zDi+lk9s6hC1FM7jgLHYIjuLgULgc8QtYnmB3tAVIYkNoKRQ75qWBeyzcoMoK8ZQmogGtC/w0g==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-transform-unicode-escapes@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.1.tgz#add0f8483dab60570d9e03cecef6c023aa8c9940"
+  integrity sha512-zZ0Poh/yy1d4jeDWpx/mNwbKJVwUYJX73q+gyh4bwtG0/iUlzdEu0sLMda8yuDFS6LBQlT/ST1SJAR6zYwXWgw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/plugin-transform-unicode-regex@^7.10.1":
+  version "7.10.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.1.tgz#6b58f2aea7b68df37ac5025d9c88752443a6b43f"
+  integrity sha512-Y/2a2W299k0VIUdbqYm9X2qS6fE0CUBhhiPpimK6byy7OJ/kORLlIX+J6UrjgNu5awvs62k+6RSslxhcvVw2Tw==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.10.1"
+    "@babel/helper-plugin-utils" "^7.10.1"
+
+"@babel/preset-env@^7.4.0":
+  version "7.10.2"
+  resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.10.2.tgz#715930f2cf8573b0928005ee562bed52fb65fdfb"
+  integrity sha512-MjqhX0RZaEgK/KueRzh+3yPSk30oqDKJ5HP5tqTSB1e2gzGS3PLy7K0BIpnp78+0anFuSwOeuCf1zZO7RzRvEA==
+  dependencies:
+    "@babel/compat-data" "^7.10.1"
+    "@babel/helper-compilation-targets" "^7.10.2"
+    "@babel/helper-module-imports" "^7.10.1"
+    "@babel/helper-plugin-utils" "^7.10.1"
+    "@babel/plugin-proposal-async-generator-functions" "^7.10.1"
+    "@babel/plugin-proposal-class-properties" "^7.10.1"
+    "@babel/plugin-proposal-dynamic-import" "^7.10.1"
+    "@babel/plugin-proposal-json-strings" "^7.10.1"
+    "@babel/plugin-proposal-nullish-coalescing-operator" "^7.10.1"
+    "@babel/plugin-proposal-numeric-separator" "^7.10.1"
+    "@babel/plugin-proposal-object-rest-spread" "^7.10.1"
+    "@babel/plugin-proposal-optional-catch-binding" "^7.10.1"
+    "@babel/plugin-proposal-optional-chaining" "^7.10.1"
+    "@babel/plugin-proposal-private-methods" "^7.10.1"
+    "@babel/plugin-proposal-unicode-property-regex" "^7.10.1"
+    "@babel/plugin-syntax-async-generators" "^7.8.0"
+    "@babel/plugin-syntax-class-properties" "^7.10.1"
+    "@babel/plugin-syntax-dynamic-import" "^7.8.0"
+    "@babel/plugin-syntax-json-strings" "^7.8.0"
+    "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0"
+    "@babel/plugin-syntax-numeric-separator" "^7.10.1"
+    "@babel/plugin-syntax-object-rest-spread" "^7.8.0"
+    "@babel/plugin-syntax-optional-catch-binding" "^7.8.0"
+    "@babel/plugin-syntax-optional-chaining" "^7.8.0"
+    "@babel/plugin-syntax-top-level-await" "^7.10.1"
+    "@babel/plugin-transform-arrow-functions" "^7.10.1"
+    "@babel/plugin-transform-async-to-generator" "^7.10.1"
+    "@babel/plugin-transform-block-scoped-functions" "^7.10.1"
+    "@babel/plugin-transform-block-scoping" "^7.10.1"
+    "@babel/plugin-transform-classes" "^7.10.1"
+    "@babel/plugin-transform-computed-properties" "^7.10.1"
+    "@babel/plugin-transform-destructuring" "^7.10.1"
+    "@babel/plugin-transform-dotall-regex" "^7.10.1"
+    "@babel/plugin-transform-duplicate-keys" "^7.10.1"
+    "@babel/plugin-transform-exponentiation-operator" "^7.10.1"
+    "@babel/plugin-transform-for-of" "^7.10.1"
+    "@babel/plugin-transform-function-name" "^7.10.1"
+    "@babel/plugin-transform-literals" "^7.10.1"
+    "@babel/plugin-transform-member-expression-literals" "^7.10.1"
+    "@babel/plugin-transform-modules-amd" "^7.10.1"