From a5730680d0f2699a96b41ed938669700949ad855 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Pasquier <contact@jbpasquier.eu>
Date: Tue, 15 Jun 2021 21:55:06 +0200
Subject: [PATCH 01/18] major: frontend federation

---
 README.md                                     |  276 ++-
 package-lock.json                             | 1515 ++++++++++++-----
 package.json                                  |    6 +
 src/components/sentry.js                      |    8 -
 src/index.js                                  |    1 +
 src/index.pug                                 |   83 +-
 src/{components => libs}/getRoute.js          |    0
 src/libs/sentry.js                            |   11 +
 src/orbit-envoy.pug                           |  198 +++
 src/orbit-router.pug                          |   89 -
 src/orbit-unify.pug                           |    0
 src/scripts/federationRegistering.js          |    7 +
 src/views/page-dashboard.pug                  |    5 -
 src/views/page-directory.pug                  |    7 -
 src/views/page-events.pug                     |   23 +-
 src/views/page-job-board.pug                  |    8 -
 src/views/page-messages.pug                   |    2 +-
 src/views/page-polls.pug                      |   10 +-
 src/views/page-profile.pug                    |    4 +-
 src/views/page-registering.pug                |    4 +-
 src/views/page-resources.pug                  |   16 +-
 .../admin/page-admin-circles-create.pug       |    2 +-
 .../admin/page-admin-circles-join.pug         |    9 +-
 .../admin/page-admin-circles-leave.pug        |   14 +-
 .../partials/admin/page-admin-circles.pug     |    4 +-
 .../admin/page-admin-projects-create.pug      |    4 +-
 .../partials/admin/page-admin-projects.pug    |   25 +-
 .../partials/circle/page-circle-chat.pug      |    2 +-
 .../partials/circle/page-circle-edit.pug      |    4 +-
 .../partials/circle/page-circle-events.pug    |    6 +-
 .../partials/circle/page-circle-polls.pug     |    8 +-
 .../partials/circle/page-circle-resources.pug |   14 +-
 .../communities/page-community-directory.pug  |    2 +-
 .../communities/page-community-edit.pug       |    8 +-
 .../communities/page-community-map.pug        |    2 +-
 .../partials/project/page-project-chat.pug    |    2 +-
 .../partials/project/page-project-edit.pug    |    4 +-
 .../project/page-project-invoices.pug         |    2 +-
 .../partials/project/page-project-picture.pug |    2 +-
 39 files changed, 1622 insertions(+), 765 deletions(-)
 delete mode 100644 src/components/sentry.js
 rename src/{components => libs}/getRoute.js (100%)
 create mode 100644 src/libs/sentry.js
 create mode 100644 src/orbit-envoy.pug
 delete mode 100644 src/orbit-router.pug
 delete mode 100644 src/orbit-unify.pug
 create mode 100644 src/scripts/federationRegistering.js
 delete mode 100644 src/views/page-dashboard.pug
 delete mode 100644 src/views/page-directory.pug
 delete mode 100644 src/views/page-job-board.pug

diff --git a/README.md b/README.md
index 08443772..8230bfeb 100644
--- a/README.md
+++ b/README.md
@@ -97,7 +97,8 @@ On `config.json`:
 {
   "client": {
     "name": "Localhost",
-    "logo": "/images/logo.webp"
+    "logo": "/images/logo.webp",
+    "server": "http://server"
   },
   "components": []
 }
@@ -107,6 +108,7 @@ Where:
 
 * `client.name` is the name of your Orbit
 * `client.logo` is an URL to an image file
+* `client.server` is the main data server of the client
 * `components` is your modules declaration registry
 
 ### Optional personalisation
@@ -226,18 +228,28 @@ Module declaration, on `config.json`:
 ```json
     {
       "type": "circles",
-      "endpoints": {
-        "get": "http://server.url/circles/",
-        "post": "http://server.url/circles/",
-        "owners": "http://server.url/users/",
-        "users": "http://server.url/users/",
+      "parameters": {
+        "dataSrc": "federation://circles/",
+        "dataSrcJoinable": "federation://circles/joinable/",
+        "owners": "federation://users/",
+        "post": "server://circles/",
+        "users": "federation://users/",
+        "noRender": "",
         "xmpp": "wss://xmpp-dev.startinblox.com/xmpp-websocket"
-      }
+      },
+      "federation": [
+        "..."
+      ],
+      "route": "circles"
     }
 ```
 
 Where:
 
+* `dataSrc`: is a container listing every circles
+* `dataSrcJoinable`: is a container listing every public & not joined circles for the user
+* `post`: is where you create new circles
+* `noRender`: will load datas only when it'll be on screen, remove it if you encounter any issue
 * `owners`: is your users container which contains valid owners
 * `users`: is your users container
 * `xmpp` is your [Prosody](https://prosody.im/) with [appropriate modules](https://git.startinblox.com/infra/prosody-modules/) configured on.
@@ -246,6 +258,8 @@ Where:
 
 You can extend circles with other components, the same way you would add them to your modules.
 
+Extensions always inherit from its parent federation.
+
 Actually it support: Events, Resources & Polls.
 
 Eg.:
@@ -253,23 +267,34 @@ Eg.:
 ```json
     {
       "type": "circles",
-      "endpoints": {
-        "get": "http://server.url/circles/",
-        "post": "http://server.url/circles/",
-        "owners": "http://server.url/users/",
-        "users": "http://server.url/users/",
+      "parameters": {
+        "dataSrc": "federation://circles/",
+        "dataSrcJoinable": "federation://circles/joinable/",
+        "owners": "federation://users/",
+        "post": "server://circles/",
+        "users": "federation://users/",
+        "noRender": "",
         "xmpp": "wss://xmpp-dev.startinblox.com/xmpp-websocket"
       },
+      "federation": [
+        "..."
+      ],
+      "route": "circles",
       "extensions": [
         {
           "type": "events",
-          "endpoints": {
-            "get": "http://server.url/events/",
-            "post": "http://server.url/events/",
-            "typeevents": "http://server.url/typeevents/",
-            "postTypeevents": "http://server.url/typeevents/",
-            "uploads": "http://server.url/upload/"
-          }
+          "parameters": {
+            "events": "federation://events/",
+            "circles": "federation://circles/",
+            "get": "federation://events/",
+            "post": "server://events/",
+            "postTypeevents": "server://typeevents/",
+            "typeevents": "federation://typeevents/",
+            "uploads": "server://upload/"
+          },
+          "federation": [
+            "..."
+          ]
         }
       ]
     }
@@ -305,10 +330,10 @@ You can activate it by changing the route to anything else than false. Some endp
 ```json
     {
       "type": "communities",
-      "endpoints": {
-        "get": "http://server/communities/",
-        "addresses": "http://server/community-addresses/",
-        "uploads": "http://server/upload/"
+      "parameters": {
+        "addresses": "federation://community-addresses/",
+        "dataSrc": "federation://communities/",
+        "uploads": "server://upload/"
       },
       "route": "communities"
     }
@@ -325,12 +350,15 @@ Module declaration, on `config.json`:
 ```json
     {
       "type": "dashboard",
-      "endpoints": {
-        "get": "http://server.url/dashboards/"
-      },
       "parameters": {
-        "target": "default"
-      }
+        "dataSrc": "server://dashboards/",
+        "noRender": "",
+        "target": false
+      },
+      "route": "dashboard",
+      "experimental": [
+        "routing"
+      ]
     }
 ```
 
@@ -338,6 +366,8 @@ A [sample fixture](https://git.startinblox.com/djangoldp-packages/djangoldp-dash
 
 You can have multiple dashboard module, see the [related documentation](https://git.startinblox.com/components/solid-dashboard#having-multiple-dashboard).
 
+You need the experimental routing enabled to have a Dashboard.
+
 ### Events
 
 The events module includes a listing of upcoming events and the capability to create new ones.
@@ -350,19 +380,21 @@ Module declaration, on `config.json`:
 ```json
     {
       "type": "events",
-      "endpoints": {
-        "get": "http://server.url/events/",
-        "post": "http://server.url/events/",
-        "regionevents": "http://server.url/regionevents/",
-        "typeevents": "http://server.url/typeevents/",
-        "postTypeevents": "http://server.url/typeevents/",
-        "uploads": "http://server.url/upload/"
-      }
-      "parameters" : {
-          "pastevents": "",
-          "visiblecheckbox": "",
-          "visibilityregions": ""
-      }
+      "parameters": {
+        "events": "federation://events/",
+        "circles": "federation://circles/",
+        "get": "federation://events/",
+        "post": "server://events/",
+        "postTypeevents": "server://typeevents/",
+        "typeevents": "federation://typeevents/",
+        "pastevents": "visible",
+        "visiblecheckbox": "visible",
+        "visibilityregions": "visible",
+        "uploads": "server://upload/"
+      },
+      "federation": [
+        "..."
+      ]
     }
 ```
 
@@ -454,8 +486,8 @@ Module declaration, on `config.json`:
       "extensions": [
         {
           "type": "invoices",
-          "endpoints": {
-            "uploads": "http://server.url/upload/"
+          "parameters": {
+            "uploads": "server://upload/"
           }
         }
       ]
@@ -478,14 +510,18 @@ Module declaration, on `config.json`:
 ```json
     {
       "type": "jobBoard",
-      "endpoints": {
-        "get": "http://server.url/job-offers/",
-        "post": "http://server.url/job-offers/",
-        "skills": "http://server.url/skills/"
-      },
       "parameters": {
+        "dataSrc": "federation://job-offers/current/",
+        "dataSrcExpired": "federation://job-offers/expired/",
+        "postDataSrc": "server://job-offers/",
+        "noRender": "",
+        "rangeSkills": "federation://skills/",
         "fields": "earnBusinessProviding"
-      }
+      },
+      "route": "job-offers",
+      "experimental": [
+        "routing"
+      ]
     }
 ```
 
@@ -493,6 +529,8 @@ Where:
 
 * `parameters.fields`: Optional set of custom fields. Notice that only `earnBusinessProviding` is already handled on djangoldp-joboffer.
 
+You need the experimental routing enabled to have a Job Board.
+
 ### Notifications
 
 The notification module adds a bell with user's notification list and a badge on each menus with how much notifications are related to this resource. You'll need:
@@ -521,7 +559,8 @@ Module declaration, on `config.json`:
 ```json
     {
       "type": "chat",
-      "endpoints": {
+      "parameters": {
+        "noRender": "",
         "xmpp": "wss://xmpp-dev.startinblox.com/xmpp-websocket"
       }
     }
@@ -542,14 +581,12 @@ On `config.json`:
 ```json
     {
       "type": "polls",
-      "endpoints": {
-        "get": "http://server.url/polls/",
+      "parameters": {
+        "dataSrc": "http://server.url/polls/",
         "post": "http://server.url/polls/",
         "pollRangeTags": "http://server.url/tags/",
         "pollRangeCircles": "http://server.url/circles/",
-        "uploads": "http://server.url/upload/"
-      },
-      "parameters": {
+        "uploads": "http://server.url/upload/",
         "displayStartEndDates": false
       }
     }
@@ -574,12 +611,17 @@ Module declaration, on `config.json`:
 ```json
     {
       "type": "projects",
-      "endpoints": {
-        "get": "http://server.url/projects/",
-        "post": "http://server.url/projects/",
-        "captains": "http://server.url/users/",
+      "parameters": {
+        "captains": "federation://users/",
+        "circles": "federation://circles/",
+        "dataSrc": "federation://projects/",
+        "dataSrcJoinable": "federation://projects/joinable/",
+        "post": "server://projects/",
+        "users": "federation://users/",
+        "noRender": "",
         "xmpp": "wss://xmpp-dev.startinblox.com/xmpp-websocket"
-      }
+      },
+      "route": "projects"
     }
 ```
 
@@ -600,8 +642,8 @@ Module declaration, on `config.json`:
 ```json
     {
       "type": "resources",
-      "endpoints": {
-        "get": "http://server.url/resources/",
+      "parameters": {
+        "dataSrc": "http://server.url/resources/",
         "post": "http://server.url/resources/",
         "types": "http://server.url/types/",
         "keywords": "http://server.url/keywords/",
@@ -629,11 +671,9 @@ Module declaration, on `config.json`:
     {
       "type": "registering",
       "parameters": {
+      "dataSrc": "server://open-communities/",
         "authority": "http://server.url/",
         "authorityName": "your-authority-indentifier"
-      },
-      "endpoints": {
-        "get": "http://server.url/open-communities/"
       }
     }
 ```
@@ -675,14 +715,25 @@ Module declaration, on `config.json`:
 ```json
     {
       "type": "profileDirectory",
-      "endpoints": {
-        "get": "http://server.url/users/",
-        "skills": "http://server.url/skills/",
-        "uploads": "http://server.url/upload/"
-      }
+      "parameters": {
+        "dataSrc": "federation://users/",
+        "rangeSkills": "federation://skills/",
+        "noRender": "",
+        "paginateBy": "30",
+        "uploads": "server://upload/"
+      },
+      "federation": [
+        "..."
+      ],
+      "route": "members",
+      "experimental": [
+        "routing"
+      ]
     }
 ```
 
+You need the experimental routing enabled to have a Profile Directory.
+
 ### Route generation
 
 Orbit will, by default, generate an unique route for every of your module. You can customize this route by setting a `route` attribute on your module declaration.
@@ -692,10 +743,10 @@ Eg. for the Users Directory:
 ```json
     {
       "type": "profileDirectory",
-      "endpoints": {
-        "get": "http://server.url/users/",
-        "skills": "http://server.url/skills/",
-        "uploads": "http://server.url/upload/"
+      "parameters": {
+        "dataSrc": "server://users/",
+        "skills": "server://skills/",
+        "uploads": "server://upload/"
       },
       "route": "directory"
     }
@@ -709,6 +760,29 @@ Some module don't need any route to be active, set `route` to `false` so.
 
 Components can get the route of a module with `window.orbit.getRoute('componentName')`.
 
+#### Experimental routing
+
+This experimental setting allow to create a view containing a `solid-*` with every parameters provided without any code.
+
+```json
+    {
+      "type": "display",
+      "parameters": {
+        "dataSrc": "server://users/",
+        "fields": "name"
+      },
+      "experimental": [
+        "routing"
+      ]
+    }
+```
+
+will provide a view with:
+
+```html
+<solid-display data-src="http://server/users/" fields="name"></solid-display>
+```
+
 #### Change the default route
 
 By default, Orbit will take a Dashboard as a default route.
@@ -720,10 +794,10 @@ Eg.:
 ```json
     {
       "type": "profileDirectory",
-      "endpoints": {
-        "get": "http://server.url/users/",
-        "skills": "http://server.url/skills/",
-        "uploads": "http://server.url/upload/"
+      "parameters": {
+        "dataSrc": "server://users/",
+        "skills": "server://skills/",
+        "uploads": "server://upload/"
       },
       "route": "directory",
       "defaultRoute": true
@@ -732,6 +806,52 @@ Eg.:
 
 If there is more than one component with this parameter, it'll be ignored.
 
+### Federation generation
+
+Any parameter of your config.json can take benefits from the source generation:
+
+`server://` will be replaced by the value of `client.server`:
+
+```json
+    {
+      "type": "awesome",
+      "parameters": {
+        "dataSrc": "server://users/"
+      }
+    }
+```
+
+`federation://` will generate a virtual federated container linking:
+
+* `client.server`
+* `client.servers`
+* `component.federation`
+* `extension.federation`, if on an extension
+
+```json
+  {
+    "client": {
+      "server": "http://serverA",
+      "servers": [
+        "http://serverB"
+      ]
+    },
+    "components": [
+      {
+        "type": "awesome",
+        "parameters": {
+          "dataSrc": "federation://users/"
+        },
+        "federation": [
+          "http://serverC"
+        ]
+      }
+    ]
+  }
+```
+
+Will result on a virtual federated container containing `http://serverA/users/`, `http://serverB/users/` and `http://serverC/users/` on data-src.
+
 ## Troubleshooting
 
 ### Circles or Projects are missing the @user list
diff --git a/package-lock.json b/package-lock.json
index f67ab7a2..a77b09a1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1906,6 +1906,27 @@
       "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
       "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
     },
+    "autoprefixer": {
+      "version": "9.8.6",
+      "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz",
+      "integrity": "sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg==",
+      "requires": {
+        "browserslist": "^4.12.0",
+        "caniuse-lite": "^1.0.30001109",
+        "colorette": "^1.2.1",
+        "normalize-range": "^0.1.2",
+        "num2fraction": "^1.2.2",
+        "postcss": "^7.0.32",
+        "postcss-value-parser": "^4.1.0"
+      },
+      "dependencies": {
+        "postcss-value-parser": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz",
+          "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ=="
+        }
+      }
+    },
     "aws-sign2": {
       "version": "0.7.0",
       "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
@@ -2337,6 +2358,13 @@
       "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=",
       "requires": {
         "callsites": "^2.0.0"
+      },
+      "dependencies": {
+        "callsites": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
+          "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA="
+        }
       }
     },
     "caller-path": {
@@ -2347,11 +2375,6 @@
         "caller-callsite": "^2.0.0"
       }
     },
-    "callsites": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
-      "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA="
-    },
     "camelcase": {
       "version": "5.3.1",
       "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
@@ -2793,17 +2816,6 @@
       "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
       "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
     },
-    "cosmiconfig": {
-      "version": "5.2.1",
-      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
-      "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
-      "requires": {
-        "import-fresh": "^2.0.0",
-        "is-directory": "^0.3.1",
-        "js-yaml": "^3.13.1",
-        "parse-json": "^4.0.0"
-      }
-    },
     "create-ecdh": {
       "version": "4.0.4",
       "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz",
@@ -2904,20 +2916,6 @@
         "randomfill": "^1.0.3"
       }
     },
-    "css-color-names": {
-      "version": "0.0.4",
-      "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
-      "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA="
-    },
-    "css-declaration-sorter": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz",
-      "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==",
-      "requires": {
-        "postcss": "^7.0.1",
-        "timsort": "^0.3.0"
-      }
-    },
     "css-modules-loader-core": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/css-modules-loader-core/-/css-modules-loader-core-1.1.0.tgz",
@@ -3029,54 +3027,6 @@
       "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
       "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="
     },
-    "cssnano": {
-      "version": "4.1.11",
-      "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.11.tgz",
-      "integrity": "sha512-6gZm2htn7xIPJOHY824ERgj8cNPgPxyCSnkXc4v7YvNW+TdVfzgngHcEhy/8D11kUWRUMbke+tC+AUcUsnMz2g==",
-      "requires": {
-        "cosmiconfig": "^5.0.0",
-        "cssnano-preset-default": "^4.0.8",
-        "is-resolvable": "^1.0.0",
-        "postcss": "^7.0.0"
-      }
-    },
-    "cssnano-preset-default": {
-      "version": "4.0.8",
-      "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.8.tgz",
-      "integrity": "sha512-LdAyHuq+VRyeVREFmuxUZR1TXjQm8QQU/ktoo/x7bz+SdOge1YKc5eMN6pRW7YWBmyq59CqYba1dJ5cUukEjLQ==",
-      "requires": {
-        "css-declaration-sorter": "^4.0.1",
-        "cssnano-util-raw-cache": "^4.0.1",
-        "postcss": "^7.0.0",
-        "postcss-calc": "^7.0.1",
-        "postcss-colormin": "^4.0.3",
-        "postcss-convert-values": "^4.0.1",
-        "postcss-discard-comments": "^4.0.2",
-        "postcss-discard-duplicates": "^4.0.2",
-        "postcss-discard-empty": "^4.0.1",
-        "postcss-discard-overridden": "^4.0.1",
-        "postcss-merge-longhand": "^4.0.11",
-        "postcss-merge-rules": "^4.0.3",
-        "postcss-minify-font-values": "^4.0.2",
-        "postcss-minify-gradients": "^4.0.2",
-        "postcss-minify-params": "^4.0.2",
-        "postcss-minify-selectors": "^4.0.2",
-        "postcss-normalize-charset": "^4.0.1",
-        "postcss-normalize-display-values": "^4.0.2",
-        "postcss-normalize-positions": "^4.0.2",
-        "postcss-normalize-repeat-style": "^4.0.2",
-        "postcss-normalize-string": "^4.0.2",
-        "postcss-normalize-timing-functions": "^4.0.2",
-        "postcss-normalize-unicode": "^4.0.1",
-        "postcss-normalize-url": "^4.0.1",
-        "postcss-normalize-whitespace": "^4.0.2",
-        "postcss-ordered-values": "^4.1.2",
-        "postcss-reduce-initial": "^4.0.3",
-        "postcss-reduce-transforms": "^4.0.2",
-        "postcss-svgo": "^4.0.3",
-        "postcss-unique-selectors": "^4.0.1"
-      }
-    },
     "cssnano-util-get-arguments": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz",
@@ -4324,6 +4274,528 @@
           "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
           "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
         },
+        "cosmiconfig": {
+          "version": "5.2.1",
+          "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
+          "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
+          "requires": {
+            "import-fresh": "^2.0.0",
+            "is-directory": "^0.3.1",
+            "js-yaml": "^3.13.1",
+            "parse-json": "^4.0.0"
+          }
+        },
+        "css-color-names": {
+          "version": "0.0.4",
+          "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
+          "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA="
+        },
+        "css-declaration-sorter": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz",
+          "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==",
+          "requires": {
+            "postcss": "^7.0.1",
+            "timsort": "^0.3.0"
+          }
+        },
+        "cssnano": {
+          "version": "4.1.11",
+          "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.11.tgz",
+          "integrity": "sha512-6gZm2htn7xIPJOHY824ERgj8cNPgPxyCSnkXc4v7YvNW+TdVfzgngHcEhy/8D11kUWRUMbke+tC+AUcUsnMz2g==",
+          "requires": {
+            "cosmiconfig": "^5.0.0",
+            "cssnano-preset-default": "^4.0.8",
+            "is-resolvable": "^1.0.0",
+            "postcss": "^7.0.0"
+          }
+        },
+        "cssnano-preset-default": {
+          "version": "4.0.8",
+          "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.8.tgz",
+          "integrity": "sha512-LdAyHuq+VRyeVREFmuxUZR1TXjQm8QQU/ktoo/x7bz+SdOge1YKc5eMN6pRW7YWBmyq59CqYba1dJ5cUukEjLQ==",
+          "requires": {
+            "css-declaration-sorter": "^4.0.1",
+            "cssnano-util-raw-cache": "^4.0.1",
+            "postcss": "^7.0.0",
+            "postcss-calc": "^7.0.1",
+            "postcss-colormin": "^4.0.3",
+            "postcss-convert-values": "^4.0.1",
+            "postcss-discard-comments": "^4.0.2",
+            "postcss-discard-duplicates": "^4.0.2",
+            "postcss-discard-empty": "^4.0.1",
+            "postcss-discard-overridden": "^4.0.1",
+            "postcss-merge-longhand": "^4.0.11",
+            "postcss-merge-rules": "^4.0.3",
+            "postcss-minify-font-values": "^4.0.2",
+            "postcss-minify-gradients": "^4.0.2",
+            "postcss-minify-params": "^4.0.2",
+            "postcss-minify-selectors": "^4.0.2",
+            "postcss-normalize-charset": "^4.0.1",
+            "postcss-normalize-display-values": "^4.0.2",
+            "postcss-normalize-positions": "^4.0.2",
+            "postcss-normalize-repeat-style": "^4.0.2",
+            "postcss-normalize-string": "^4.0.2",
+            "postcss-normalize-timing-functions": "^4.0.2",
+            "postcss-normalize-unicode": "^4.0.1",
+            "postcss-normalize-url": "^4.0.1",
+            "postcss-normalize-whitespace": "^4.0.2",
+            "postcss-ordered-values": "^4.1.2",
+            "postcss-reduce-initial": "^4.0.3",
+            "postcss-reduce-transforms": "^4.0.2",
+            "postcss-svgo": "^4.0.3",
+            "postcss-unique-selectors": "^4.0.1"
+          }
+        },
+        "import-fresh": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
+          "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
+          "requires": {
+            "caller-path": "^2.0.0",
+            "resolve-from": "^3.0.0"
+          }
+        },
+        "is-absolute-url": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz",
+          "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY="
+        },
+        "normalize-url": {
+          "version": "3.3.0",
+          "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz",
+          "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg=="
+        },
+        "parse-json": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+          "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+          "requires": {
+            "error-ex": "^1.3.1",
+            "json-parse-better-errors": "^1.0.1"
+          }
+        },
+        "postcss-calc": {
+          "version": "7.0.5",
+          "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz",
+          "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==",
+          "requires": {
+            "postcss": "^7.0.27",
+            "postcss-selector-parser": "^6.0.2",
+            "postcss-value-parser": "^4.0.2"
+          }
+        },
+        "postcss-colormin": {
+          "version": "4.0.3",
+          "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz",
+          "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==",
+          "requires": {
+            "browserslist": "^4.0.0",
+            "color": "^3.0.0",
+            "has": "^1.0.0",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          },
+          "dependencies": {
+            "postcss-value-parser": {
+              "version": "3.3.1",
+              "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+              "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
+            }
+          }
+        },
+        "postcss-convert-values": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz",
+          "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==",
+          "requires": {
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          },
+          "dependencies": {
+            "postcss-value-parser": {
+              "version": "3.3.1",
+              "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+              "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
+            }
+          }
+        },
+        "postcss-discard-comments": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz",
+          "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==",
+          "requires": {
+            "postcss": "^7.0.0"
+          }
+        },
+        "postcss-discard-duplicates": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz",
+          "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==",
+          "requires": {
+            "postcss": "^7.0.0"
+          }
+        },
+        "postcss-discard-empty": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz",
+          "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==",
+          "requires": {
+            "postcss": "^7.0.0"
+          }
+        },
+        "postcss-discard-overridden": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz",
+          "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==",
+          "requires": {
+            "postcss": "^7.0.0"
+          }
+        },
+        "postcss-merge-longhand": {
+          "version": "4.0.11",
+          "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz",
+          "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==",
+          "requires": {
+            "css-color-names": "0.0.4",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0",
+            "stylehacks": "^4.0.0"
+          },
+          "dependencies": {
+            "postcss-value-parser": {
+              "version": "3.3.1",
+              "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+              "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
+            }
+          }
+        },
+        "postcss-merge-rules": {
+          "version": "4.0.3",
+          "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz",
+          "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==",
+          "requires": {
+            "browserslist": "^4.0.0",
+            "caniuse-api": "^3.0.0",
+            "cssnano-util-same-parent": "^4.0.0",
+            "postcss": "^7.0.0",
+            "postcss-selector-parser": "^3.0.0",
+            "vendors": "^1.0.0"
+          },
+          "dependencies": {
+            "postcss-selector-parser": {
+              "version": "3.1.2",
+              "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz",
+              "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==",
+              "requires": {
+                "dot-prop": "^5.2.0",
+                "indexes-of": "^1.0.1",
+                "uniq": "^1.0.1"
+              }
+            }
+          }
+        },
+        "postcss-minify-font-values": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz",
+          "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==",
+          "requires": {
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          },
+          "dependencies": {
+            "postcss-value-parser": {
+              "version": "3.3.1",
+              "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+              "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
+            }
+          }
+        },
+        "postcss-minify-gradients": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz",
+          "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==",
+          "requires": {
+            "cssnano-util-get-arguments": "^4.0.0",
+            "is-color-stop": "^1.0.0",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          },
+          "dependencies": {
+            "postcss-value-parser": {
+              "version": "3.3.1",
+              "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+              "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
+            }
+          }
+        },
+        "postcss-minify-params": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz",
+          "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==",
+          "requires": {
+            "alphanum-sort": "^1.0.0",
+            "browserslist": "^4.0.0",
+            "cssnano-util-get-arguments": "^4.0.0",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0",
+            "uniqs": "^2.0.0"
+          },
+          "dependencies": {
+            "postcss-value-parser": {
+              "version": "3.3.1",
+              "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+              "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
+            }
+          }
+        },
+        "postcss-minify-selectors": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz",
+          "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==",
+          "requires": {
+            "alphanum-sort": "^1.0.0",
+            "has": "^1.0.0",
+            "postcss": "^7.0.0",
+            "postcss-selector-parser": "^3.0.0"
+          },
+          "dependencies": {
+            "postcss-selector-parser": {
+              "version": "3.1.2",
+              "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz",
+              "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==",
+              "requires": {
+                "dot-prop": "^5.2.0",
+                "indexes-of": "^1.0.1",
+                "uniq": "^1.0.1"
+              }
+            }
+          }
+        },
+        "postcss-normalize-charset": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz",
+          "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==",
+          "requires": {
+            "postcss": "^7.0.0"
+          }
+        },
+        "postcss-normalize-display-values": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz",
+          "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==",
+          "requires": {
+            "cssnano-util-get-match": "^4.0.0",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          },
+          "dependencies": {
+            "postcss-value-parser": {
+              "version": "3.3.1",
+              "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+              "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
+            }
+          }
+        },
+        "postcss-normalize-positions": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz",
+          "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==",
+          "requires": {
+            "cssnano-util-get-arguments": "^4.0.0",
+            "has": "^1.0.0",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          },
+          "dependencies": {
+            "postcss-value-parser": {
+              "version": "3.3.1",
+              "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+              "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
+            }
+          }
+        },
+        "postcss-normalize-repeat-style": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz",
+          "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==",
+          "requires": {
+            "cssnano-util-get-arguments": "^4.0.0",
+            "cssnano-util-get-match": "^4.0.0",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          },
+          "dependencies": {
+            "postcss-value-parser": {
+              "version": "3.3.1",
+              "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+              "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
+            }
+          }
+        },
+        "postcss-normalize-string": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz",
+          "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==",
+          "requires": {
+            "has": "^1.0.0",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          },
+          "dependencies": {
+            "postcss-value-parser": {
+              "version": "3.3.1",
+              "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+              "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
+            }
+          }
+        },
+        "postcss-normalize-timing-functions": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz",
+          "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==",
+          "requires": {
+            "cssnano-util-get-match": "^4.0.0",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          },
+          "dependencies": {
+            "postcss-value-parser": {
+              "version": "3.3.1",
+              "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+              "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
+            }
+          }
+        },
+        "postcss-normalize-unicode": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz",
+          "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==",
+          "requires": {
+            "browserslist": "^4.0.0",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          },
+          "dependencies": {
+            "postcss-value-parser": {
+              "version": "3.3.1",
+              "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+              "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
+            }
+          }
+        },
+        "postcss-normalize-url": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz",
+          "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==",
+          "requires": {
+            "is-absolute-url": "^2.0.0",
+            "normalize-url": "^3.0.0",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          },
+          "dependencies": {
+            "postcss-value-parser": {
+              "version": "3.3.1",
+              "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+              "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
+            }
+          }
+        },
+        "postcss-normalize-whitespace": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz",
+          "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==",
+          "requires": {
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          },
+          "dependencies": {
+            "postcss-value-parser": {
+              "version": "3.3.1",
+              "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+              "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
+            }
+          }
+        },
+        "postcss-ordered-values": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz",
+          "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==",
+          "requires": {
+            "cssnano-util-get-arguments": "^4.0.0",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          },
+          "dependencies": {
+            "postcss-value-parser": {
+              "version": "3.3.1",
+              "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+              "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
+            }
+          }
+        },
+        "postcss-reduce-initial": {
+          "version": "4.0.3",
+          "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz",
+          "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==",
+          "requires": {
+            "browserslist": "^4.0.0",
+            "caniuse-api": "^3.0.0",
+            "has": "^1.0.0",
+            "postcss": "^7.0.0"
+          }
+        },
+        "postcss-reduce-transforms": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz",
+          "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==",
+          "requires": {
+            "cssnano-util-get-match": "^4.0.0",
+            "has": "^1.0.0",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          },
+          "dependencies": {
+            "postcss-value-parser": {
+              "version": "3.3.1",
+              "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+              "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
+            }
+          }
+        },
+        "postcss-svgo": {
+          "version": "4.0.3",
+          "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.3.tgz",
+          "integrity": "sha512-NoRbrcMWTtUghzuKSoIm6XV+sJdvZ7GZSc3wdBN0W19FTtp2ko8NqLsgoh/m9CzNhU3KLPvQmjIwtaNFkaFTvw==",
+          "requires": {
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0",
+            "svgo": "^1.0.0"
+          },
+          "dependencies": {
+            "postcss-value-parser": {
+              "version": "3.3.1",
+              "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+              "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
+            }
+          }
+        },
+        "postcss-unique-selectors": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz",
+          "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==",
+          "requires": {
+            "alphanum-sort": "^1.0.0",
+            "postcss": "^7.0.0",
+            "uniqs": "^2.0.0"
+          }
+        },
+        "postcss-value-parser": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz",
+          "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ=="
+        },
         "posthtml": {
           "version": "0.13.4",
           "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.13.4.tgz",
@@ -4341,6 +4813,33 @@
             "htmlparser2": "^3.9.2"
           }
         },
+        "resolve-from": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
+          "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
+        },
+        "stylehacks": {
+          "version": "4.0.3",
+          "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz",
+          "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==",
+          "requires": {
+            "browserslist": "^4.0.0",
+            "postcss": "^7.0.0",
+            "postcss-selector-parser": "^3.0.0"
+          },
+          "dependencies": {
+            "postcss-selector-parser": {
+              "version": "3.1.2",
+              "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz",
+              "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==",
+              "requires": {
+                "dot-prop": "^5.2.0",
+                "indexes-of": "^1.0.1",
+                "uniq": "^1.0.1"
+              }
+            }
+          }
+        },
         "terser": {
           "version": "4.8.0",
           "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
@@ -4429,15 +4928,6 @@
       "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
       "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
     },
-    "import-fresh": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
-      "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
-      "requires": {
-        "caller-path": "^2.0.0",
-        "resolve-from": "^3.0.0"
-      }
-    },
     "indent-string": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
@@ -4469,11 +4959,6 @@
       "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==",
       "dev": true
     },
-    "is-absolute-url": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz",
-      "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY="
-    },
     "is-accessor-descriptor": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
@@ -4545,6 +5030,13 @@
         "hsla-regex": "^1.0.0",
         "rgb-regex": "^1.0.1",
         "rgba-regex": "^1.0.0"
+      },
+      "dependencies": {
+        "css-color-names": {
+          "version": "0.0.4",
+          "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
+          "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA="
+        }
       }
     },
     "is-core-module": {
@@ -5517,10 +6009,10 @@
       "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
       "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
     },
-    "normalize-url": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz",
-      "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg=="
+    "normalize-range": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
+      "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI="
     },
     "normalize.css": {
       "version": "8.0.1",
@@ -5544,6 +6036,11 @@
         "boolbase": "~1.0.0"
       }
     },
+    "num2fraction": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz",
+      "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4="
+    },
     "nwsapi": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz",
@@ -5891,6 +6388,17 @@
           "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
           "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
         },
+        "cosmiconfig": {
+          "version": "5.2.1",
+          "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
+          "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
+          "requires": {
+            "import-fresh": "^2.0.0",
+            "is-directory": "^0.3.1",
+            "js-yaml": "^3.13.1",
+            "parse-json": "^4.0.0"
+          }
+        },
         "cross-spawn": {
           "version": "6.0.5",
           "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
@@ -5903,11 +6411,455 @@
             "which": "^1.2.9"
           }
         },
+        "css-color-names": {
+          "version": "0.0.4",
+          "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
+          "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA="
+        },
+        "css-declaration-sorter": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz",
+          "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==",
+          "requires": {
+            "postcss": "^7.0.1",
+            "timsort": "^0.3.0"
+          }
+        },
+        "cssnano": {
+          "version": "4.1.11",
+          "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.11.tgz",
+          "integrity": "sha512-6gZm2htn7xIPJOHY824ERgj8cNPgPxyCSnkXc4v7YvNW+TdVfzgngHcEhy/8D11kUWRUMbke+tC+AUcUsnMz2g==",
+          "requires": {
+            "cosmiconfig": "^5.0.0",
+            "cssnano-preset-default": "^4.0.8",
+            "is-resolvable": "^1.0.0",
+            "postcss": "^7.0.0"
+          }
+        },
+        "cssnano-preset-default": {
+          "version": "4.0.8",
+          "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.8.tgz",
+          "integrity": "sha512-LdAyHuq+VRyeVREFmuxUZR1TXjQm8QQU/ktoo/x7bz+SdOge1YKc5eMN6pRW7YWBmyq59CqYba1dJ5cUukEjLQ==",
+          "requires": {
+            "css-declaration-sorter": "^4.0.1",
+            "cssnano-util-raw-cache": "^4.0.1",
+            "postcss": "^7.0.0",
+            "postcss-calc": "^7.0.1",
+            "postcss-colormin": "^4.0.3",
+            "postcss-convert-values": "^4.0.1",
+            "postcss-discard-comments": "^4.0.2",
+            "postcss-discard-duplicates": "^4.0.2",
+            "postcss-discard-empty": "^4.0.1",
+            "postcss-discard-overridden": "^4.0.1",
+            "postcss-merge-longhand": "^4.0.11",
+            "postcss-merge-rules": "^4.0.3",
+            "postcss-minify-font-values": "^4.0.2",
+            "postcss-minify-gradients": "^4.0.2",
+            "postcss-minify-params": "^4.0.2",
+            "postcss-minify-selectors": "^4.0.2",
+            "postcss-normalize-charset": "^4.0.1",
+            "postcss-normalize-display-values": "^4.0.2",
+            "postcss-normalize-positions": "^4.0.2",
+            "postcss-normalize-repeat-style": "^4.0.2",
+            "postcss-normalize-string": "^4.0.2",
+            "postcss-normalize-timing-functions": "^4.0.2",
+            "postcss-normalize-unicode": "^4.0.1",
+            "postcss-normalize-url": "^4.0.1",
+            "postcss-normalize-whitespace": "^4.0.2",
+            "postcss-ordered-values": "^4.1.2",
+            "postcss-reduce-initial": "^4.0.3",
+            "postcss-reduce-transforms": "^4.0.2",
+            "postcss-svgo": "^4.0.3",
+            "postcss-unique-selectors": "^4.0.1"
+          }
+        },
+        "has-flag": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
+        },
+        "import-fresh": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
+          "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
+          "requires": {
+            "caller-path": "^2.0.0",
+            "resolve-from": "^3.0.0"
+          }
+        },
+        "is-absolute-url": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz",
+          "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY="
+        },
+        "normalize-url": {
+          "version": "3.3.0",
+          "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz",
+          "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg=="
+        },
+        "parse-json": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+          "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+          "requires": {
+            "error-ex": "^1.3.1",
+            "json-parse-better-errors": "^1.0.1"
+          }
+        },
         "path-key": {
           "version": "2.0.1",
           "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
           "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
-        }
+        },
+        "postcss": {
+          "version": "7.0.36",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz",
+          "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==",
+          "requires": {
+            "chalk": "^2.4.2",
+            "source-map": "^0.6.1",
+            "supports-color": "^6.1.0"
+          }
+        },
+        "postcss-calc": {
+          "version": "7.0.5",
+          "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz",
+          "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==",
+          "requires": {
+            "postcss": "^7.0.27",
+            "postcss-selector-parser": "^6.0.2",
+            "postcss-value-parser": "^4.0.2"
+          },
+          "dependencies": {
+            "postcss-value-parser": {
+              "version": "4.1.0",
+              "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz",
+              "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ=="
+            }
+          }
+        },
+        "postcss-colormin": {
+          "version": "4.0.3",
+          "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz",
+          "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==",
+          "requires": {
+            "browserslist": "^4.0.0",
+            "color": "^3.0.0",
+            "has": "^1.0.0",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          }
+        },
+        "postcss-convert-values": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz",
+          "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==",
+          "requires": {
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          }
+        },
+        "postcss-discard-comments": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz",
+          "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==",
+          "requires": {
+            "postcss": "^7.0.0"
+          }
+        },
+        "postcss-discard-duplicates": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz",
+          "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==",
+          "requires": {
+            "postcss": "^7.0.0"
+          }
+        },
+        "postcss-discard-empty": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz",
+          "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==",
+          "requires": {
+            "postcss": "^7.0.0"
+          }
+        },
+        "postcss-discard-overridden": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz",
+          "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==",
+          "requires": {
+            "postcss": "^7.0.0"
+          }
+        },
+        "postcss-merge-longhand": {
+          "version": "4.0.11",
+          "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz",
+          "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==",
+          "requires": {
+            "css-color-names": "0.0.4",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0",
+            "stylehacks": "^4.0.0"
+          }
+        },
+        "postcss-merge-rules": {
+          "version": "4.0.3",
+          "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz",
+          "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==",
+          "requires": {
+            "browserslist": "^4.0.0",
+            "caniuse-api": "^3.0.0",
+            "cssnano-util-same-parent": "^4.0.0",
+            "postcss": "^7.0.0",
+            "postcss-selector-parser": "^3.0.0",
+            "vendors": "^1.0.0"
+          },
+          "dependencies": {
+            "postcss-selector-parser": {
+              "version": "3.1.2",
+              "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz",
+              "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==",
+              "requires": {
+                "dot-prop": "^5.2.0",
+                "indexes-of": "^1.0.1",
+                "uniq": "^1.0.1"
+              }
+            }
+          }
+        },
+        "postcss-minify-font-values": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz",
+          "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==",
+          "requires": {
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          }
+        },
+        "postcss-minify-gradients": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz",
+          "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==",
+          "requires": {
+            "cssnano-util-get-arguments": "^4.0.0",
+            "is-color-stop": "^1.0.0",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          }
+        },
+        "postcss-minify-params": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz",
+          "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==",
+          "requires": {
+            "alphanum-sort": "^1.0.0",
+            "browserslist": "^4.0.0",
+            "cssnano-util-get-arguments": "^4.0.0",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0",
+            "uniqs": "^2.0.0"
+          }
+        },
+        "postcss-minify-selectors": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz",
+          "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==",
+          "requires": {
+            "alphanum-sort": "^1.0.0",
+            "has": "^1.0.0",
+            "postcss": "^7.0.0",
+            "postcss-selector-parser": "^3.0.0"
+          },
+          "dependencies": {
+            "postcss-selector-parser": {
+              "version": "3.1.2",
+              "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz",
+              "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==",
+              "requires": {
+                "dot-prop": "^5.2.0",
+                "indexes-of": "^1.0.1",
+                "uniq": "^1.0.1"
+              }
+            }
+          }
+        },
+        "postcss-normalize-charset": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz",
+          "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==",
+          "requires": {
+            "postcss": "^7.0.0"
+          }
+        },
+        "postcss-normalize-display-values": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz",
+          "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==",
+          "requires": {
+            "cssnano-util-get-match": "^4.0.0",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          }
+        },
+        "postcss-normalize-positions": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz",
+          "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==",
+          "requires": {
+            "cssnano-util-get-arguments": "^4.0.0",
+            "has": "^1.0.0",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          }
+        },
+        "postcss-normalize-repeat-style": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz",
+          "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==",
+          "requires": {
+            "cssnano-util-get-arguments": "^4.0.0",
+            "cssnano-util-get-match": "^4.0.0",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          }
+        },
+        "postcss-normalize-string": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz",
+          "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==",
+          "requires": {
+            "has": "^1.0.0",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          }
+        },
+        "postcss-normalize-timing-functions": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz",
+          "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==",
+          "requires": {
+            "cssnano-util-get-match": "^4.0.0",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          }
+        },
+        "postcss-normalize-unicode": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz",
+          "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==",
+          "requires": {
+            "browserslist": "^4.0.0",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          }
+        },
+        "postcss-normalize-url": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz",
+          "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==",
+          "requires": {
+            "is-absolute-url": "^2.0.0",
+            "normalize-url": "^3.0.0",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          }
+        },
+        "postcss-normalize-whitespace": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz",
+          "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==",
+          "requires": {
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          }
+        },
+        "postcss-ordered-values": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz",
+          "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==",
+          "requires": {
+            "cssnano-util-get-arguments": "^4.0.0",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          }
+        },
+        "postcss-reduce-initial": {
+          "version": "4.0.3",
+          "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz",
+          "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==",
+          "requires": {
+            "browserslist": "^4.0.0",
+            "caniuse-api": "^3.0.0",
+            "has": "^1.0.0",
+            "postcss": "^7.0.0"
+          }
+        },
+        "postcss-reduce-transforms": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz",
+          "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==",
+          "requires": {
+            "cssnano-util-get-match": "^4.0.0",
+            "has": "^1.0.0",
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0"
+          }
+        },
+        "postcss-svgo": {
+          "version": "4.0.3",
+          "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.3.tgz",
+          "integrity": "sha512-NoRbrcMWTtUghzuKSoIm6XV+sJdvZ7GZSc3wdBN0W19FTtp2ko8NqLsgoh/m9CzNhU3KLPvQmjIwtaNFkaFTvw==",
+          "requires": {
+            "postcss": "^7.0.0",
+            "postcss-value-parser": "^3.0.0",
+            "svgo": "^1.0.0"
+          }
+        },
+        "postcss-unique-selectors": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz",
+          "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==",
+          "requires": {
+            "alphanum-sort": "^1.0.0",
+            "postcss": "^7.0.0",
+            "uniqs": "^2.0.0"
+          }
+        },
+        "resolve-from": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
+          "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
+        },
+        "stylehacks": {
+          "version": "4.0.3",
+          "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz",
+          "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==",
+          "requires": {
+            "browserslist": "^4.0.0",
+            "postcss": "^7.0.0",
+            "postcss-selector-parser": "^3.0.0"
+          },
+          "dependencies": {
+            "postcss-selector-parser": {
+              "version": "3.1.2",
+              "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz",
+              "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==",
+              "requires": {
+                "dot-prop": "^5.2.0",
+                "indexes-of": "^1.0.1",
+                "uniq": "^1.0.1"
+              }
+            }
+          }
+        },
+        "supports-color": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+          "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        }
       }
     },
     "parcel-plugin-sw-cache": {
@@ -5942,15 +6894,6 @@
         "safe-buffer": "^5.1.1"
       }
     },
-    "parse-json": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
-      "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
-      "requires": {
-        "error-ex": "^1.3.1",
-        "json-parse-better-errors": "^1.0.1"
-      }
-    },
     "parse5": {
       "version": "5.1.0",
       "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz",
@@ -6046,9 +6989,9 @@
       "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs="
     },
     "postcss": {
-      "version": "7.0.35",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz",
-      "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==",
+      "version": "7.0.36",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz",
+      "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==",
       "requires": {
         "chalk": "^2.4.2",
         "source-map": "^0.6.1",
@@ -6070,168 +7013,6 @@
         }
       }
     },
-    "postcss-calc": {
-      "version": "7.0.5",
-      "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz",
-      "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==",
-      "requires": {
-        "postcss": "^7.0.27",
-        "postcss-selector-parser": "^6.0.2",
-        "postcss-value-parser": "^4.0.2"
-      },
-      "dependencies": {
-        "postcss-value-parser": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz",
-          "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ=="
-        }
-      }
-    },
-    "postcss-colormin": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz",
-      "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==",
-      "requires": {
-        "browserslist": "^4.0.0",
-        "color": "^3.0.0",
-        "has": "^1.0.0",
-        "postcss": "^7.0.0",
-        "postcss-value-parser": "^3.0.0"
-      }
-    },
-    "postcss-convert-values": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz",
-      "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==",
-      "requires": {
-        "postcss": "^7.0.0",
-        "postcss-value-parser": "^3.0.0"
-      }
-    },
-    "postcss-discard-comments": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz",
-      "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==",
-      "requires": {
-        "postcss": "^7.0.0"
-      }
-    },
-    "postcss-discard-duplicates": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz",
-      "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==",
-      "requires": {
-        "postcss": "^7.0.0"
-      }
-    },
-    "postcss-discard-empty": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz",
-      "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==",
-      "requires": {
-        "postcss": "^7.0.0"
-      }
-    },
-    "postcss-discard-overridden": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz",
-      "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==",
-      "requires": {
-        "postcss": "^7.0.0"
-      }
-    },
-    "postcss-merge-longhand": {
-      "version": "4.0.11",
-      "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz",
-      "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==",
-      "requires": {
-        "css-color-names": "0.0.4",
-        "postcss": "^7.0.0",
-        "postcss-value-parser": "^3.0.0",
-        "stylehacks": "^4.0.0"
-      }
-    },
-    "postcss-merge-rules": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz",
-      "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==",
-      "requires": {
-        "browserslist": "^4.0.0",
-        "caniuse-api": "^3.0.0",
-        "cssnano-util-same-parent": "^4.0.0",
-        "postcss": "^7.0.0",
-        "postcss-selector-parser": "^3.0.0",
-        "vendors": "^1.0.0"
-      },
-      "dependencies": {
-        "postcss-selector-parser": {
-          "version": "3.1.2",
-          "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz",
-          "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==",
-          "requires": {
-            "dot-prop": "^5.2.0",
-            "indexes-of": "^1.0.1",
-            "uniq": "^1.0.1"
-          }
-        }
-      }
-    },
-    "postcss-minify-font-values": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz",
-      "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==",
-      "requires": {
-        "postcss": "^7.0.0",
-        "postcss-value-parser": "^3.0.0"
-      }
-    },
-    "postcss-minify-gradients": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz",
-      "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==",
-      "requires": {
-        "cssnano-util-get-arguments": "^4.0.0",
-        "is-color-stop": "^1.0.0",
-        "postcss": "^7.0.0",
-        "postcss-value-parser": "^3.0.0"
-      }
-    },
-    "postcss-minify-params": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz",
-      "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==",
-      "requires": {
-        "alphanum-sort": "^1.0.0",
-        "browserslist": "^4.0.0",
-        "cssnano-util-get-arguments": "^4.0.0",
-        "postcss": "^7.0.0",
-        "postcss-value-parser": "^3.0.0",
-        "uniqs": "^2.0.0"
-      }
-    },
-    "postcss-minify-selectors": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz",
-      "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==",
-      "requires": {
-        "alphanum-sort": "^1.0.0",
-        "has": "^1.0.0",
-        "postcss": "^7.0.0",
-        "postcss-selector-parser": "^3.0.0"
-      },
-      "dependencies": {
-        "postcss-selector-parser": {
-          "version": "3.1.2",
-          "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz",
-          "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==",
-          "requires": {
-            "dot-prop": "^5.2.0",
-            "indexes-of": "^1.0.1",
-            "uniq": "^1.0.1"
-          }
-        }
-      }
-    },
     "postcss-modules-extract-imports": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz",
@@ -6367,128 +7148,6 @@
         }
       }
     },
-    "postcss-normalize-charset": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz",
-      "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==",
-      "requires": {
-        "postcss": "^7.0.0"
-      }
-    },
-    "postcss-normalize-display-values": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz",
-      "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==",
-      "requires": {
-        "cssnano-util-get-match": "^4.0.0",
-        "postcss": "^7.0.0",
-        "postcss-value-parser": "^3.0.0"
-      }
-    },
-    "postcss-normalize-positions": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz",
-      "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==",
-      "requires": {
-        "cssnano-util-get-arguments": "^4.0.0",
-        "has": "^1.0.0",
-        "postcss": "^7.0.0",
-        "postcss-value-parser": "^3.0.0"
-      }
-    },
-    "postcss-normalize-repeat-style": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz",
-      "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==",
-      "requires": {
-        "cssnano-util-get-arguments": "^4.0.0",
-        "cssnano-util-get-match": "^4.0.0",
-        "postcss": "^7.0.0",
-        "postcss-value-parser": "^3.0.0"
-      }
-    },
-    "postcss-normalize-string": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz",
-      "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==",
-      "requires": {
-        "has": "^1.0.0",
-        "postcss": "^7.0.0",
-        "postcss-value-parser": "^3.0.0"
-      }
-    },
-    "postcss-normalize-timing-functions": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz",
-      "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==",
-      "requires": {
-        "cssnano-util-get-match": "^4.0.0",
-        "postcss": "^7.0.0",
-        "postcss-value-parser": "^3.0.0"
-      }
-    },
-    "postcss-normalize-unicode": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz",
-      "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==",
-      "requires": {
-        "browserslist": "^4.0.0",
-        "postcss": "^7.0.0",
-        "postcss-value-parser": "^3.0.0"
-      }
-    },
-    "postcss-normalize-url": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz",
-      "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==",
-      "requires": {
-        "is-absolute-url": "^2.0.0",
-        "normalize-url": "^3.0.0",
-        "postcss": "^7.0.0",
-        "postcss-value-parser": "^3.0.0"
-      }
-    },
-    "postcss-normalize-whitespace": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz",
-      "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==",
-      "requires": {
-        "postcss": "^7.0.0",
-        "postcss-value-parser": "^3.0.0"
-      }
-    },
-    "postcss-ordered-values": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz",
-      "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==",
-      "requires": {
-        "cssnano-util-get-arguments": "^4.0.0",
-        "postcss": "^7.0.0",
-        "postcss-value-parser": "^3.0.0"
-      }
-    },
-    "postcss-reduce-initial": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz",
-      "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==",
-      "requires": {
-        "browserslist": "^4.0.0",
-        "caniuse-api": "^3.0.0",
-        "has": "^1.0.0",
-        "postcss": "^7.0.0"
-      }
-    },
-    "postcss-reduce-transforms": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz",
-      "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==",
-      "requires": {
-        "cssnano-util-get-match": "^4.0.0",
-        "has": "^1.0.0",
-        "postcss": "^7.0.0",
-        "postcss-value-parser": "^3.0.0"
-      }
-    },
     "postcss-selector-parser": {
       "version": "6.0.4",
       "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz",
@@ -6500,26 +7159,6 @@
         "util-deprecate": "^1.0.2"
       }
     },
-    "postcss-svgo": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.3.tgz",
-      "integrity": "sha512-NoRbrcMWTtUghzuKSoIm6XV+sJdvZ7GZSc3wdBN0W19FTtp2ko8NqLsgoh/m9CzNhU3KLPvQmjIwtaNFkaFTvw==",
-      "requires": {
-        "postcss": "^7.0.0",
-        "postcss-value-parser": "^3.0.0",
-        "svgo": "^1.0.0"
-      }
-    },
-    "postcss-unique-selectors": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz",
-      "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==",
-      "requires": {
-        "alphanum-sort": "^1.0.0",
-        "postcss": "^7.0.0",
-        "uniqs": "^2.0.0"
-      }
-    },
     "postcss-value-parser": {
       "version": "3.3.1",
       "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
@@ -7050,11 +7689,6 @@
         "path-parse": "^1.0.6"
       }
     },
-    "resolve-from": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
-      "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
-    },
     "resolve-url": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
@@ -7831,28 +8465,6 @@
       "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
       "dev": true
     },
-    "stylehacks": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz",
-      "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==",
-      "requires": {
-        "browserslist": "^4.0.0",
-        "postcss": "^7.0.0",
-        "postcss-selector-parser": "^3.0.0"
-      },
-      "dependencies": {
-        "postcss-selector-parser": {
-          "version": "3.1.2",
-          "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz",
-          "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==",
-          "requires": {
-            "dot-prop": "^5.2.0",
-            "indexes-of": "^1.0.1",
-            "uniq": "^1.0.1"
-          }
-        }
-      }
-    },
     "supports-color": {
       "version": "8.1.1",
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
@@ -8125,11 +8737,26 @@
           "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
           "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
         },
+        "has-flag": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
+        },
         "is-absolute-url": {
           "version": "3.0.3",
           "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz",
           "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q=="
         },
+        "postcss": {
+          "version": "7.0.36",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz",
+          "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==",
+          "requires": {
+            "chalk": "^2.4.2",
+            "source-map": "^0.6.1",
+            "supports-color": "^6.1.0"
+          }
+        },
         "postcss-selector-parser": {
           "version": "6.0.2",
           "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz",
@@ -8139,6 +8766,14 @@
             "indexes-of": "^1.0.1",
             "uniq": "^1.0.1"
           }
+        },
+        "supports-color": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+          "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
         }
       }
     },
diff --git a/package.json b/package.json
index 0a320897..58268ec1 100644
--- a/package.json
+++ b/package.json
@@ -56,8 +56,14 @@
     ],
     "clearDist": false
   },
+  "postcss": {
+    "plugins": {
+      "autoprefixer": {}
+    }
+  },
   "dependencies": {
     "@startinblox/orbit-styling-framework": "^1.9.9",
+    "autoprefixer": "^9.8.6",
     "cross-env": "^7.0.3",
     "fs-extra": "^10.0.0",
     "normalize.css": "^8.0.1",
diff --git a/src/components/sentry.js b/src/components/sentry.js
deleted file mode 100644
index bdbb7f2a..00000000
--- a/src/components/sentry.js
+++ /dev/null
@@ -1,8 +0,0 @@
-if (typeof Sentry !== 'undefined') {
-    Sentry.init({
-        dsn: 'https://b4b29557689049a39168599577adb940@sentry.startinblox.com/4',
-        integrations: [new Sentry.Integrations.BrowserTracing()],
-        environment: document.location.hostname,
-        tracesSampleRate: 0.2,
-    });
-}
\ No newline at end of file
diff --git a/src/index.js b/src/index.js
index 170e7c98..c67f6fba 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,2 +1,3 @@
 //- Automatically import every scripts
+import './libs/**/*.js';
 import './scripts/**/*.js';
\ No newline at end of file
diff --git a/src/index.pug b/src/index.pug
index c7e00f69..969592e5 100644
--- a/src/index.pug
+++ b/src/index.pug
@@ -25,11 +25,8 @@ html(lang="en")
 
     script(src="https://browser.sentry-cdn.com/5.25.0/bundle.tracing.min.js" defer)
 
-    include orbit-router.pug
-    include orbit-unify.pug
+    include orbit-envoy.pug
 
-    script(type="module" src="/components/getRoute.js" defer)
-    script(type="module" src="/components/sentry.js" defer)
     script(type="module" src="/components/orbit-auto-login.js" defer)
     script(type="module" src="/components/orbit-reactivity.js" defer)
     script(type="module" src="/components/sw-toolbox.js" defer)
@@ -88,7 +85,27 @@ html(lang="en")
               id=component.route
               data-view=component.route
               hidden
-            )&attributes({"no-render": component.noRender})
+            )
+
+              //- Experimental routing
+                  Codeless component loading
+                  Eg.: 
+                  ```
+                    {
+                      "type": "awesome",
+                      "parameters": {
+                        "dataSrc": "server://some-model/"
+                      },
+                      "experimental": [
+                        "routing"
+                      ]
+                    }
+                  ```
+                  will generate a view vith a <solid-awesome data-src="http://server/some-model/"></solid-awesome>
+              if component.experimental 
+                if component.experimental.includes('routing')
+                  .scrollbar-content.whitespace-normal.padding-top-xlarge.padding-right-xsmall.padding-bottom-xlarge.padding-left-xsmall.sm-padding-top-medium
+                    #{"solid-"+component.type}&attributes(component.attributes)
 
               if component.type == "about"
                 include views/page-about.pug
@@ -102,66 +119,52 @@ html(lang="en")
                 .with-sidebar.whitespace-normal.jsMobileContentSidebarControl
                   orbit-reactivity(bind-user nested-field='inbox' target-src="store://user.circles")
                   orbit-reactivity(bind-user nested-field="circles" target-src="store://user")
-                  orbit-reactivity(data-src=`${component.endpoints.get}joinable/` target-src=`${component.endpoints.get}`)
-                  orbit-reactivity(data-src=`${component.endpoints.post}` target-src=`${component.endpoints.get}`)
-                  orbit-reactivity(bind-user nested-field="circles" target-src=`${component.endpoints.post}`)
-                  orbit-reactivity(bind-user nested-field="circles" target-src=`${component.endpoints.post}joinable/`)
-                  orbit-reactivity(data-src=`${component.endpoints.get}joinable/` target-src=`${component.endpoints.get}`)
-                  orbit-reactivity(bind-user nested-field="circles" target-src=`${component.endpoints.get}`)
-                  orbit-reactivity(bind-user nested-field="circles" target-src=`${component.endpoints.get}joinable/`)
+                  orbit-reactivity(data-src=`${component.parameters.dataSrcJoinable}` target-src=`${component.parameters.dataSrc}`)
+                  orbit-reactivity(data-src=`${component.parameters.post}` target-src=`${component.parameters.dataSrc}`)
+                  orbit-reactivity(bind-user nested-field="circles" target-src=`${component.parameters.post}`)
+                  orbit-reactivity(bind-user nested-field="circles" target-src=`${component.parameters.dataSrcJoinable}`)
+                  orbit-reactivity(bind-user nested-field="circles" target-src=`${component.parameters.dataSrc}`)
                   include views/page-circle.pug
 
               if component.type == "communities"
                 .scrollbar-content.whitespace-normal
                   include views/page-communities.pug
 
-              if component.type == "dashboard"
-                .scrollbar-content
-                  include views/page-dashboard.pug
-
               if component.type == "events"
                 .scrollbar-content.bg-color-white
                   include views/page-events.pug
 
               if component.type == "jobBoard"
-                .scrollbar-content
-                  orbit-reactivity(data-src=`${component.endpoints.post}current/` target-src=`${component.endpoints.get}`)
-                  orbit-reactivity(data-src=`${component.endpoints.post}expired/` target-src=`${component.endpoints.get}`)
-                  orbit-reactivity(data-src=`${component.endpoints.post}` target-src=`${component.endpoints.get}`)
-                  orbit-reactivity(data-src=`${component.endpoints.get}current/` target-src=`${component.endpoints.get}`)
-                  orbit-reactivity(data-src=`${component.endpoints.get}current/` target-src=`${component.endpoints.get}expired/`)
-                  orbit-reactivity(data-src=`${component.endpoints.get}expired/` target-src=`${component.endpoints.get}`)
-                  orbit-reactivity(bind-user nested-field="joboffers" target-src=`${component.endpoints.get}expired/`)
-                  orbit-reactivity(bind-user nested-field="joboffers" target-src=`${component.endpoints.get}`)
-                  orbit-reactivity(bind-user nested-field="joboffers" target-src=`${component.endpoints.get}current/`)
-                  include views/page-job-board.pug
+                orbit-reactivity(data-src=`${component.parameters.dataSrcExpired}` target-src=`${component.parameters.dataSrc}`)
+                orbit-reactivity(data-src=`${component.parameters.post}` target-src=`${component.parameters.dataSrc}`)
+                orbit-reactivity(data-src=`${component.parameters.dataSrcExpired}` target-src=`${component.parameters.post}`)
+                orbit-reactivity(bind-user nested-field="joboffers" target-src=`${component.parameters.dataSrc}`)
+                orbit-reactivity(bind-user nested-field="joboffers" target-src=`${component.parameters.dataSrcExpired}`)
+                orbit-reactivity(bind-user nested-field="joboffers" target-src=`${component.parameters.post}`)
 
               if component.type == "polls"
                 .scrollbar-content
                   include views/page-polls.pug
 
+              if component.type == "profileDirectory"
+                orbit-reactivity(bind-user nested-field="profile" target-src="store://user")
+                orbit-reactivity(bind-user nested-field="account" target-src="store://user")
+
               if component.type == "projects"
                 .with-sidebar.whitespace-normal.jsMobileContentSidebarControl
                   orbit-reactivity(bind-user nested-field='inbox' target-src="store://user.projects")
                   orbit-reactivity(bind-user nested-field="projects" target-src="store://user")
-                  orbit-reactivity(data-src=`${component.endpoints.post}joinable/` target-src=`${component.endpoints.get}`)
-                  orbit-reactivity(data-src=`${component.endpoints.post}` target-src=`${component.endpoints.get}`)
-                  orbit-reactivity(bind-user nested-field="projects" target-src=`${component.endpoints.post}`)
-                  orbit-reactivity(bind-user nested-field="projects" target-src=`${component.endpoints.post}joinable/`)
-                  orbit-reactivity(data-src=`${component.endpoints.get}joinable/` target-src=`${component.endpoints.get}`)
-                  orbit-reactivity(bind-user nested-field="projects" target-src=`${component.endpoints.get}`)
-                  orbit-reactivity(bind-user nested-field="projects" target-src=`${component.endpoints.get}joinable/`)
+                  orbit-reactivity(data-src=`${component.parameters.dataSrcJoinable}` target-src=`${component.parameters.dataSrc}`)
+                  orbit-reactivity(data-src=`${component.parameters.dataSrcJoinable}` target-src=`${component.parameters.post}`)
+                  orbit-reactivity(data-src=`${component.parameters.post}` target-src=`${component.parameters.dataSrc}`)
+                  orbit-reactivity(bind-user nested-field="projects" target-src=`${component.parameters.post}`)
+                  orbit-reactivity(bind-user nested-field="projects" target-src=`${component.parameters.dataSrcJoinable}`)
+                  orbit-reactivity(bind-user nested-field="projects" target-src=`${component.parameters.dataSrc}`)
                   include views/page-project.pug
 
               if component.type == "resources"
                 .scrollbar-content.bg-color-white
                   include views/page-resources.pug
-
-              if component.type == "profileDirectory"
-                .scrollbar-content
-                  orbit-reactivity(bind-user nested-field="profile" target-src="store://user")
-                  orbit-reactivity(bind-user nested-field="account" target-src="store://user")
-                  include views/page-directory.pug
           else
             //- Components declaration without any route (`route`="false") but that need some code declaration
 
diff --git a/src/components/getRoute.js b/src/libs/getRoute.js
similarity index 100%
rename from src/components/getRoute.js
rename to src/libs/getRoute.js
diff --git a/src/libs/sentry.js b/src/libs/sentry.js
new file mode 100644
index 00000000..ce56b0fe
--- /dev/null
+++ b/src/libs/sentry.js
@@ -0,0 +1,11 @@
+document.addEventListener("DOMContentLoaded", () => {
+  if (typeof Sentry !== 'undefined') {
+    Sentry.init({
+      dsn: 'https://b4b29557689049a39168599577adb940@sentry.startinblox.com/4',
+      integrations: [new Sentry.Integrations.BrowserTracing()],
+      environment: document.location.hostname,
+      tracesSampleRate: 0.2,
+    });
+    window.orbit.sentry = Sentry;
+  }
+});
\ No newline at end of file
diff --git a/src/orbit-envoy.pug b/src/orbit-envoy.pug
new file mode 100644
index 00000000..dc95b44e
--- /dev/null
+++ b/src/orbit-envoy.pug
@@ -0,0 +1,198 @@
+//-
+  Orbit Envoy - Collision-free route generation & federation management for latter usage
+
+  Create a window.orbit.components, accessible by all components with the route declaration.
+  Components can also get benefits from the `getRoute` function
+  Eg.
+  ```
+    window.orbit.getRoute('chat', true)
+    window.orbit.getRoute('ffb39ad020645') // Where uxnzsa is the uniq of the component
+  ```
+  will return the route of the first chat component, if exists, or triggers an error.
+
+
+  Okay. This file really needs a cleanup now... Emergency, have to release for Friday...
+-
+  let routes = new Set();
+  const getRoute = (type, returnFirst = false) => {
+    let availables = components.filter(c => c.type == type || c.uniq == type);
+    components.forEach(c => {
+      if (c.extensions) {
+        c.extensions.forEach(e => {
+          if (e.type == type || e.uniq == type) {
+            availables.push(e);
+          }
+        });
+      }
+    });
+    if (availables.length > 1) {
+      if (returnFirst) {
+        return availables[0].route;
+      } else {
+        return availables[availables.length - 1].route;
+      }
+    } else if (availables.length < 1) {
+      console.error(`No component found for route ${type}`);
+    } else {
+      return availables[0].route;
+    }
+  }
+  const getComponent = (type, returnFirst = false) => {
+    let availables = components.filter(c=>c.type==type);
+    if(availables.length > 1) {
+      if(returnFirst) {
+        return availables[0];
+      } else {
+        console.error(`Too much components availables for type ${type}`);
+      }
+    } else if(availables.length < 1) {
+      console.error(`No component found for type ${type}`);
+    } else {
+      return availables[0];
+    }
+  }
+  const generateUrl = (federation, path) => {
+    let result = [];
+    for(let target of federation) {
+      result.push({
+        "@id": target + path.replace(/federation:\//, ''),
+        "@type": "sib:federatedContainer"
+      });
+    }
+    return result;
+  }
+  var federations = {};
+  let federationRegistering = "";
+
+for component of components
+  -
+    if(typeof component.route === 'undefined') {
+      component.route = component.type;
+    }
+    component.uniq = Math.random().toString(16).slice(2);
+    if(component.route) {
+      let route = component.route;
+      if (routes.has(component.route)) {
+        route += "-" + component.uniq;
+      }
+      routes.add(route);
+      component.route = route;
+    }
+    if(component.extensions) {
+      for(extension of component.extensions) {
+        if(typeof extension.route === 'undefined') {
+          extension.route = extension.type;
+        }
+        extension.uniq = Math.random().toString(16).slice(2);
+        if(extension.route) {
+          let route = extension.route;
+          if (routes.has(extension.route)) {
+            route += "-" + extension.uniq;
+          }
+          routes.add(route);
+          extension.route = route;
+        }
+      }
+    }
+    if(component.parameters) {
+      let federation = new Set();
+      if(client.server) {
+        federation.add(client.server);
+      }
+      if(client.servers) {
+        for(server of client.servers) {
+          federation.add(server);
+        }
+      }
+      if(component.federation) {
+        for(target of component.federation) {
+          federation.add(target);
+        }
+      }
+      component.federation = [...federation];
+      for(const [attribute, path] of Object.entries(component.parameters)) {
+        if(typeof path === 'string') {
+          if(path.startsWith('federation://')) {
+            federations[`store://local.${component.uniq}/${attribute}/`] = {
+              "@context": "https://cdn.happy-dev.fr/owl/hdcontext.jsonld",
+              "@type": "ldp:Container",
+              "@id": `store://local.${component.uniq}/${attribute}/`,
+              "ldp:contains": generateUrl(federation, path),
+              "permissions": [{"mode": {"@type": "view"}}]
+            };
+            component.parameters[attribute] = `store://local.${component.uniq}/${attribute}/`;
+          }
+          if(path.startsWith('server://')) {
+            component.parameters[attribute] = client.server + path.replace(/server:\//, '');
+          }
+        }
+      }
+      /* Rewrite every parameters to kebab-case */
+      let rewriteParameters = {};
+      for(const [attribute, value] of Object.entries(component.parameters)) {
+        rewriteParameters[attribute.replace(/((?<=[a-z\d])[A-Z]|(?<=[A-Z\d])[A-Z](?=[a-z]))/g, '-$1').toLowerCase()] = value;
+      }
+      component.attributes = rewriteParameters;
+      component.attributes.route = component.route;
+      component.attributes.uniq = component.uniq;
+    }
+    if(component.extensions) {
+      for(extension of component.extensions) {
+        if(extension.parameters) {
+          let federation = new Set();
+          if(client.server) {
+            federation.add(client.server);
+          }
+          if(client.servers) {
+            for(server of client.servers) {
+              federation.add(server);
+            }
+          }
+          if(component.federation) {
+            for(target of component.federation) {
+              federation.add(target);
+            }
+          }
+          if(extension.federation) {
+            for(target of extension.federation) {
+              federation.add(target);
+            }
+          }
+          extension.federation = [...federation];
+          for(const [attribute, path] of Object.entries(extension.parameters)) {
+            if(typeof path === 'string') {
+              if(path.startsWith('federation://')) {
+                federations[`store://local.${extension.uniq}/${attribute}/`] = {
+                  "@context": "https://cdn.happy-dev.fr/owl/hdcontext.jsonld",
+                  "@type": "ldp:Container",
+                  "@id": `store://local.${extension.uniq}/${attribute}/`,
+                  "ldp:contains": generateUrl(federation, path),
+                  "permissions": [{"mode": {"@type": "view"}}]
+                };
+                extension.parameters[attribute] = `store://local.${extension.uniq}/${attribute}/`;
+              }
+              if(path.startsWith('server://')) {
+                extension.parameters[attribute] = client.server + path.replace(/server:\//, '');
+              }
+            }
+          }
+          /* Rewrite every parameters to kebab-case */
+          let rewriteParameters = {};
+          for(const [attribute, value] of Object.entries(extension.parameters)) {
+            rewriteParameters[attribute.replace(/((?<=[a-z\d])[A-Z]|(?<=[A-Z\d])[A-Z](?=[a-z]))/g, '-$1').toLowerCase()] = value;
+          }
+          extension.attributes = rewriteParameters;
+          extension.attributes.uniq = extension.uniq;
+        }
+      }
+    }
+
+-
+  const defaultComponent = components.filter(e=>e.defaultRoute != undefined);
+  let defaultRoute = "dashboard";
+  if(defaultComponent.length == 1) {
+    defaultRoute = defaultComponent[0].uniq;
+  }
+
+- const orbitComponents = `window.orbit={};window.orbit.components = ${JSON.stringify(components)};window.orbit.federations = ${JSON.stringify(federations)};window.orbit.defaultRoute = "${defaultRoute}";window.orbit.client = ${JSON.stringify(client)};window.hubl = window.orbit;`;
+script!=orbitComponents
\ No newline at end of file
diff --git a/src/orbit-router.pug b/src/orbit-router.pug
deleted file mode 100644
index fccf1256..00000000
--- a/src/orbit-router.pug
+++ /dev/null
@@ -1,89 +0,0 @@
-//-
-  Orbit router declaration for latter generation
-  Create a window.orbit.components, accessible by all components with the route declaration.
-  Components can also get benefits from the `getRoute` function
-  Eg.
-  ```
-    window.orbit.getRoute('chat', true)
-    window.orbit.getRoute('uxnzsa') // Where uxnzsa is the uniq of the component
-  ```
-  will return the route of the first chat component, if exists, or triggers an error.
--
-  let routes = new Set();
-  const getRoute = (type, returnFirst = false) => {
-    let availables = components.filter(c => c.type == type || c.uniq == type);
-    components.forEach(c => {
-      if (c.extensions) {
-        c.extensions.forEach(e => {
-          if (e.type == type || e.uniq == type) {
-            availables.push(e);
-          }
-        });
-      }
-    });
-    if (availables.length > 1) {
-      if (returnFirst) {
-        return availables[0].route;
-      } else {
-        return availables[availables.length - 1].route;
-      }
-    } else if (availables.length < 1) {
-      console.error(`No component found for route ${type}`);
-    } else {
-      return availables[0].route;
-    }
-  }
-  const getComponent = (type, returnFirst = false) => {
-    let availables = components.filter(c=>c.type==type);
-    if(availables.length > 1) {
-      if(returnFirst) {
-        return availables[0];
-      } else {
-        console.error(`Too much components availables for type ${type}`);
-      }
-    } else if(availables.length < 1) {
-      console.error(`No component found for type ${type}`);
-    } else {
-      return availables[0];
-    }
-  }
-
-for component of components
-  -
-    if(typeof component.route === 'undefined') {
-      component.route = component.type;
-    }
-    if(component.route) {
-      component.uniq = Math.random().toString(16).slice(2);
-      let route = component.route;
-      if (routes.has(component.route)) {
-        route += "-" + component.uniq;
-      }
-      routes.add(route);
-      component.route = route;
-    }
-  if(component.extensions)
-    for extension of component.extensions
-      -
-        if(typeof extension.route === 'undefined') {
-          extension.route = extension.type;
-        }
-        if(extension.route) {
-          extension.uniq = Math.random().toString(16).slice(2);
-          let route = extension.route;
-          if (routes.has(extension.route)) {
-            route += "-" + extension.uniq;
-          }
-          routes.add(route);
-          extension.route = route;
-        }
-
--
-  const defaultComponent = components.filter(e=>e.defaultRoute != undefined);
-  let defaultRoute = "dashboard";
-  if(defaultComponent.length == 1) {
-    defaultRoute = defaultComponent[0].uniq;
-  }
-
-- const orbitComponents = `window.orbit={};window.orbit.components = ${JSON.stringify(components)};window.orbit.defaultRoute = "${defaultRoute}";window.orbit.client = ${JSON.stringify(client)};window.hubl = window.orbit;`;
-script!=orbitComponents
\ No newline at end of file
diff --git a/src/orbit-unify.pug b/src/orbit-unify.pug
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/scripts/federationRegistering.js b/src/scripts/federationRegistering.js
new file mode 100644
index 00000000..0dbf7164
--- /dev/null
+++ b/src/scripts/federationRegistering.js
@@ -0,0 +1,7 @@
+document.addEventListener("DOMContentLoaded", () => {
+  for(const [uniq, federation] of Object.entries(window.orbit.federations)) {
+    if (sibStore && "setLocalData" in sibStore) {
+      sibStore.setLocalData(federation, uniq);
+    }
+  }
+});
\ No newline at end of file
diff --git a/src/views/page-dashboard.pug b/src/views/page-dashboard.pug
deleted file mode 100644
index 6d666730..00000000
--- a/src/views/page-dashboard.pug
+++ /dev/null
@@ -1,5 +0,0 @@
-div.padding-top-xlarge.padding-right-xsmall.padding-bottom-xlarge.padding-left-xsmall.sm-padding-top-medium.bg-color-grey.whitespace-normal
-  solid-dashboard(
-    data-src=component.endpoints.get
-    uniq=component.uniq
-  )&attributes({target:component.parameters ? component.parameters.target : false})
diff --git a/src/views/page-directory.pug b/src/views/page-directory.pug
deleted file mode 100644
index 635c5ddf..00000000
--- a/src/views/page-directory.pug
+++ /dev/null
@@ -1,7 +0,0 @@
-div.bg-color-grey.padding-top-xlarge.padding-right-small.padding-bottom-xlarge.padding-left-small.whitespace-normal
-  solid-directory(
-    data-src=`${component.endpoints.get}`
-    range-skills=`${component.endpoints.skills}`
-    uniq=component.uniq
-    paginate-by="30"
-  )
diff --git a/src/views/page-events.pug b/src/views/page-events.pug
index 5b5d7b39..b7ee2091 100644
--- a/src/views/page-events.pug
+++ b/src/views/page-events.pug
@@ -1,12 +1,11 @@
-div.whitespace-normal
-  solid-event(
-    data-src=`${component.endpoints.events}`
-    past-events=`${component.parameters ? component.parameters.pastevents : "hidden"}`
-    visiblecheckbox=`${component.parameters ? component.parameters.visiblecheckbox : "hidden"}`
-    visibilityregions=`${component.parameters ? component.parameters.visibilityregions : "hidden"}`
-    range-event-region=`${component.endpoints.regionevents}`
-    range-event-type=`${component.endpoints.typeevents}`
-    range-event-circle=`${getComponent('circles').endpoints.get}/`
-    upload-dir=`${component.endpoints.uploads}`
-    id-prefix='default'
-  )
+solid-event(
+  data-src=`${component.parameters.events}`
+  past-events=`${component.parameters.pastevents || "hidden"}`
+  visiblecheckbox=`${component.parameters.visiblecheckbox || "hidden"}`
+  visibilityregions=`${component.parameters.visibilityregions || "hidden"}`
+  range-event-region=`${component.parameters.regionevents}`
+  range-event-type=`${component.parameters.typeevents}`
+  range-event-circle=`${component.parameters.circles}`
+  upload-dir=`${component.parameters.uploads}`
+  id-prefix='default'
+)
diff --git a/src/views/page-job-board.pug b/src/views/page-job-board.pug
deleted file mode 100644
index 474feafc..00000000
--- a/src/views/page-job-board.pug
+++ /dev/null
@@ -1,8 +0,0 @@
-solid-job-board(
-  data-src=`${component.endpoints.get}`
-  post-data-src=`${component.endpoints.post}`
-  range-skills=`${component.endpoints.skills}`
-  uniq=component.uniq
-)&attributes({
-  "fields": component.parameters ? component.parameters.fields : false
-})
diff --git a/src/views/page-messages.pug b/src/views/page-messages.pug
index a3b2e465..e5e91226 100644
--- a/src/views/page-messages.pug
+++ b/src/views/page-messages.pug
@@ -25,7 +25,7 @@ div.segment.full.padding-top-small.padding-right-large.padding-bottom-small.padd
   solid-xmpp-chat(
     data-authentication='login'
     data-auto-login='true'
-    data-websocket-url=component.endpoints.xmpp
+    data-websocket-url=component.parameters.xmpp
     bind-resources
     uniq=component.uniq
   )
diff --git a/src/views/page-polls.pug b/src/views/page-polls.pug
index a1691313..04fbad83 100644
--- a/src/views/page-polls.pug
+++ b/src/views/page-polls.pug
@@ -1,9 +1,9 @@
 solid-poll(
-  data-src=component.endpoints.get
-  data-dest=component.endpoints.post
-  range-tags=component.endpoints.pollRangeTags
-  range-circles=component.endpoints.pollRangeCircles
-  upload-dir=component.endpoints.uploads
+  data-src=component.parameters.dataSrc
+  data-dest=component.parameters.post
+  range-tags=component.parameters.pollRangeTags
+  range-circles=component.parameters.pollRangeCircles
+  upload-dir=component.parameters.uploads
   uniq=component.uniq
 )&attributes({
   "display-start-end-dates": component.parameters ? component.parameters.displayStartEndDates : false
diff --git a/src/views/page-profile.pug b/src/views/page-profile.pug
index c536ab0a..ae4673f8 100644
--- a/src/views/page-profile.pug
+++ b/src/views/page-profile.pug
@@ -1,6 +1,6 @@
 solid-profile(
   bind-user
-  upload-src=`${getComponent('profileDirectory', true).endpoints.uploads}`
-  range-skills=`${getComponent('profileDirectory', true).endpoints.skills}`
+  upload-src=`${getComponent('profileDirectory', true).parameters.uploads}`
+  range-skills=`${getComponent('profileDirectory', true).parameters.skills}`
   uniq=`${getComponent('profileDirectory', true).uniq}`
 )
diff --git a/src/views/page-registering.pug b/src/views/page-registering.pug
index e9b4a11a..025058e6 100644
--- a/src/views/page-registering.pug
+++ b/src/views/page-registering.pug
@@ -28,14 +28,14 @@
               widget-logo='orbit-index-community-logo'
               widget-name='orbit-index-community-text'
             )
-      if getComponent('registering').endpoints.get
+      if getComponent('registering').parameters.dataSrc
         div.loader#orbit-index-community-selector-loader
           div
           div
           div
           div
         solid-display.community-flex-container(
-          data-src=`${getComponent('registering').endpoints.get}`
+          data-src=`${getComponent('registering').parameters.dataSrc}`
           fields='action'
           action-action='action'
           widget-action='orbit-index-select-community'
diff --git a/src/views/page-resources.pug b/src/views/page-resources.pug
index c29dac97..a6dfa466 100644
--- a/src/views/page-resources.pug
+++ b/src/views/page-resources.pug
@@ -1,14 +1,14 @@
 div.whitespace-normal
-  solid-resource(data-src=`${component.endpoints.get}`
-      post-data-src=`${component.endpoints.post}`
-      range-resource-type=`${component.endpoints.types}`
-      post-data-type-src=`${component.endpoints.postTypes}`
-      range-resource-keyword=`${component.endpoints.keywords}`
-      post-data-keyword-src=`${component.endpoints.postKeywords}`
-      circles=`${component.endpoints.circles}/`
+  solid-resource(data-src=`${component.parameters.dataSrc}`
+      post-data-src=`${component.parameters.post}`
+      range-resource-type=`${component.parameters.types}`
+      post-data-type-src=`${component.parameters.postTypes}`
+      range-resource-keyword=`${component.parameters.keywords}`
+      post-data-keyword-src=`${component.parameters.postKeywords}`
+      circles=`${component.parameters.circles}`
       associated-circle-label=""
       data-trans=`associated-circle-label=${component.parameters && component.parameters.associatedName ? component.parameters.associatedName : 'circle.extensions.associated'}`
-      upload-dir=`${component.endpoints.uploads}`
+      upload-dir=`${component.parameters.uploads}`
       id-prefix='default'
       uniq=component.uniq
   )
diff --git a/src/views/partials/admin/page-admin-circles-create.pug b/src/views/partials/admin/page-admin-circles-create.pug
index 9e5aade7..9f53b126 100644
--- a/src/views/partials/admin/page-admin-circles-create.pug
+++ b/src/views/partials/admin/page-admin-circles-create.pug
@@ -14,7 +14,7 @@ div.segment.full.padding-large.sm-padding-xsmall.sm-padding-top-medium.whitespac
     div
 
   solid-form.form(
-    data-src=`${getComponent('circles').endpoints.post}`
+    data-src=`${getComponent('circles').parameters.post}`
 
     fields='status, community.community, name, subtitle, description, help'
     required-status
diff --git a/src/views/partials/admin/page-admin-circles-join.pug b/src/views/partials/admin/page-admin-circles-join.pug
index eabdb9f2..7f8025f3 100644
--- a/src/views/partials/admin/page-admin-circles-join.pug
+++ b/src/views/partials/admin/page-admin-circles-join.pug
@@ -13,10 +13,9 @@ solid-widget(name=`orbit-admin-circle-join-button`)
       class-submit-button="add-member-button segment sm-full margin-top-xsmall text-xsmall children-link-button children-link-text-bold children-link-text-uppercase children-link-reversed color-secondary bordered children-button-icon children-icon-arrow-right-circle children-icon-small children-icon-margin-right-xsmall"
       data-trans='submit-button=circle.list.buttonJoin'
     )
-    orbit-reactivity(data-src=`${getComponent('circles').endpoints.get}` target-src='${value}')
-    orbit-reactivity(data-src=`${getComponent('circles').endpoints.get}joinable/` target-src='${value}')
-    orbit-reactivity(data-src=`${getComponent('circles').endpoints.post}` target-src='${value}')
-    orbit-reactivity(data-src=`${getComponent('circles').endpoints.post}joinable/` target-src='${value}')
+    orbit-reactivity(data-src=`${getComponent('circles').parameters.dataSrc}` target-src='${value}')
+    orbit-reactivity(data-src=`${getComponent('circles').parameters.dataSrcJoinable}` target-src='${value}')
+    orbit-reactivity(data-src=`${getComponent('circles').parameters.post}` target-src='${value}')
     orbit-reactivity(bind-user nested-field="circles" target-src='${value}')
 
 include page-admin-circles.pug
@@ -24,7 +23,7 @@ include page-admin-circles.pug
 div.segment.full.padding-small.padding-top-xsmall.sm-padding-xsmall.whitespace-normal
   solid-display(
     class='segment full children children-quarter sm-children-full children-margin-bottom-medium sm-children-margin-bottom-xsmall children-padding-right-xsmall children-padding-left-xsmall sm-children-padding-none sm-whitespace-normal masonry pagination'
-    data-src=`${getComponent('circles').endpoints.get}joinable/`
+    data-src=`${getComponent('circles').parameters.dataSrcJoinable}`
     fields='segment1(segment2(community.community.logo), segment3(name, subtitle, counter, members))'
     filtered-by=`admin-circle-filter-${page}`
     order-by='name'
diff --git a/src/views/partials/admin/page-admin-circles-leave.pug b/src/views/partials/admin/page-admin-circles-leave.pug
index 1440cd66..934ee5ea 100644
--- a/src/views/partials/admin/page-admin-circles-leave.pug
+++ b/src/views/partials/admin/page-admin-circles-leave.pug
@@ -1,9 +1,8 @@
 solid-widget(name=`leave-circle-reactivity`)
   template
-    orbit-reactivity(data-src=`${getComponent('circles').endpoints.get}` target-src='${value}')
-    orbit-reactivity(data-src=`${getComponent('circles').endpoints.get}joinable/` target-src='${value}')
-    orbit-reactivity(data-src=`${getComponent('circles').endpoints.post}` target-src='${value}')
-    orbit-reactivity(data-src=`${getComponent('circles').endpoints.post}joinable/` target-src='${value}')
+    orbit-reactivity(data-src=`${getComponent('circles').parameters.dataSrc}` target-src='${value}')
+    orbit-reactivity(data-src=`${getComponent('circles').parameters.dataSrcJoinable}` target-src='${value}')
+    orbit-reactivity(data-src=`${getComponent('circles').parameters.post}` target-src='${value}')
     orbit-reactivity(bind-user nested-field="circles" target-src='${value}')
 
 solid-widget(name=`orbit-admin-circle-leave-button`)
@@ -14,10 +13,9 @@ solid-widget(name=`orbit-admin-circle-leave-button`)
       data-label=''
       data-trans='data-label=circle.list.buttonQuit'
     )
-    orbit-reactivity(data-src=`${getComponent('circles').endpoints.get}` target-src='${src}')
-    orbit-reactivity(data-src=`${getComponent('circles').endpoints.get}joinable/` target-src='${src}')
-    orbit-reactivity(data-src=`${getComponent('circles').endpoints.post}` target-src='${src}')
-    orbit-reactivity(data-src=`${getComponent('circles').endpoints.post}joinable/` target-src='${src}')
+    orbit-reactivity(data-src=`${getComponent('circles').parameters.dataSrc}` target-src='${src}')
+    orbit-reactivity(data-src=`${getComponent('circles').parameters.dataSrcJoinable}` target-src='${src}')
+    orbit-reactivity(data-src=`${getComponent('circles').parameters.post}` target-src='${src}')
     orbit-reactivity(bind-user nested-field="circles" target-src='${src}')
     solid-display(
       data-src="${src}"
diff --git a/src/views/partials/admin/page-admin-circles.pug b/src/views/partials/admin/page-admin-circles.pug
index 5e5159e7..34cdffcd 100644
--- a/src/views/partials/admin/page-admin-circles.pug
+++ b/src/views/partials/admin/page-admin-circles.pug
@@ -16,7 +16,7 @@ div.segment.full.padding-large.padding-top-medium.padding-bottom-xsmall.sm-paddi
     div.segment.half.sm-full
       h3.text-color-heading.text-semibold.text-letter-spacing-large(data-trans='circle.list.subTitle')
     div.segment.half.sm-full.text-right
-      solid-ac-checker(data-src=`${getComponent('circles').endpoints.post}`, permission='acl:Append')
+      solid-ac-checker(data-src=`${getComponent('circles').parameters.post}`, permission='acl:Append')
         solid-link(
           class='segment sm-full button text-xsmall text-bold text-uppercase text-center reversed color-secondary bordered button-icon icon icon-margin-right-xsmall icon-plus'
           next=`admin-${getRoute('circles', true)}-create`
@@ -43,7 +43,7 @@ div.segment.full.padding-large.padding-top-medium.padding-bottom-xsmall.sm-paddi
         span(data-trans='circle.list.tabJoin')
         solid-display(
           class="margin-left-xxsmall"
-          data-src=`${getComponent('circles').endpoints.get}joinable/`
+          data-src=`${getComponent('circles').parameters.dataSrcJoinable}`
           fields=""
           counter-template="(${counter})"
           filtered-by=`admin-circle-filter-${page}`
diff --git a/src/views/partials/admin/page-admin-projects-create.pug b/src/views/partials/admin/page-admin-projects-create.pug
index f58ad027..022bb9b5 100644
--- a/src/views/partials/admin/page-admin-projects-create.pug
+++ b/src/views/partials/admin/page-admin-projects-create.pug
@@ -14,9 +14,9 @@ div.segment.full.padding-large.sm-padding-xsmall.sm-padding-top-medium.whitespac
     div
 
   solid-form.form(
-    data-src=`${getComponent('projects').endpoints.post}`
+    data-src=`${getComponent('projects').parameters.post}`
 
-    range-captain=`${getComponent('projects').endpoints.captains}`
+    range-captain=`${getComponent('projects').parameters.captains}`
     
     required-status
     required-customer.name
diff --git a/src/views/partials/admin/page-admin-projects.pug b/src/views/partials/admin/page-admin-projects.pug
index 813c2bb5..32c121e6 100644
--- a/src/views/partials/admin/page-admin-projects.pug
+++ b/src/views/partials/admin/page-admin-projects.pug
@@ -10,7 +10,7 @@ div.segment.full.padding-large.sm-padding-xsmall.sm-padding-top-medium.whitespac
     div.segment.half.sm-full
       h3.text-color-heading.text-semibold.text-letter-spacing-large(data-trans='project.list.subTitle')
     div.segment.half.sm-full.text-right
-      solid-ac-checker(data-src=`${getComponent('projects').endpoints.post}`, permission='acl:Append')
+      solid-ac-checker(data-src=`${getComponent('projects').parameters.post}`, permission='acl:Append')
         solid-link(
           class='segment sm-full button text-xsmall text-bold text-uppercase text-center reversed color-secondary bordered button-icon icon icon-margin-right-xsmall icon-plus'
           next=`admin-${getRoute('projects', true)}-create`
@@ -40,10 +40,9 @@ div.segment.full.padding-large.sm-padding-xsmall.sm-padding-top-medium.whitespac
 
       solid-widget(name=`leave-project-reactivity`)
         template
-          orbit-reactivity(data-src=`${getComponent('projects').endpoints.get}` target-src='${value}')
-          orbit-reactivity(data-src=`${getComponent('projects').endpoints.get}joinable/` target-src='${value}')
-          orbit-reactivity(data-src=`${getComponent('projects').endpoints.post}` target-src='${value}')
-          orbit-reactivity(data-src=`${getComponent('projects').endpoints.post}joinable/` target-src='${value}')
+          orbit-reactivity(data-src=`${getComponent('projects').parameters.dataSrc}` target-src='${value}')
+          orbit-reactivity(data-src=`${getComponent('projects').parameters.dataSrcJoinable}` target-src='${value}')
+          orbit-reactivity(data-src=`${getComponent('projects').parameters.post}` target-src='${value}')
           orbit-reactivity(bind-user nested-field="projects" target-src='${value}')
 
       solid-widget(name=`orbit-admin-project-leave-button`)
@@ -54,10 +53,9 @@ div.segment.full.padding-large.sm-padding-xsmall.sm-padding-top-medium.whitespac
             data-label=''
             data-trans='data-label=project.list.buttonQuit'
           )
-          orbit-reactivity(data-src=`${getComponent('projects').endpoints.get}` target-src='${src}')
-          orbit-reactivity(data-src=`${getComponent('projects').endpoints.get}joinable/` target-src='${src}')
-          orbit-reactivity(data-src=`${getComponent('projects').endpoints.post}` target-src='${src}')
-          orbit-reactivity(data-src=`${getComponent('projects').endpoints.post}joinable/` target-src='${src}')
+          orbit-reactivity(data-src=`${getComponent('projects').parameters.dataSrc}` target-src='${src}')
+          orbit-reactivity(data-src=`${getComponent('projects').parameters.dataSrcJoinable}` target-src='${src}')
+          orbit-reactivity(data-src=`${getComponent('projects').parameters.post}` target-src='${src}')
           orbit-reactivity(bind-user nested-field="projects" target-src='${src}')
           solid-display(
             data-src="${src}"
@@ -119,17 +117,16 @@ div.segment.full.padding-large.sm-padding-xsmall.sm-padding-top-medium.whitespac
             class-submit-button="add-member-button segment margin-top-xsmall text-xsmall children-link-button children-link-text-bold children-link-text-uppercase children-link-reversed color-secondary bordered children-button-icon children-icon-arrow-right-circle children-icon-small children-icon-margin-right-xsmall"
             data-trans='submit-button=project.list.buttonJoin'
           )
-          orbit-reactivity(data-src=`${getComponent('projects').endpoints.get}` target-src='${value}')
-          orbit-reactivity(data-src=`${getComponent('projects').endpoints.get}joinable/` target-src='${value}')
-          orbit-reactivity(data-src=`${getComponent('projects').endpoints.post}` target-src='${value}')
-          orbit-reactivity(data-src=`${getComponent('projects').endpoints.post}joinable/` target-src='${value}')
+          orbit-reactivity(data-src=`${getComponent('projects').parameters.dataSrc}` target-src='${value}')
+          orbit-reactivity(data-src=`${getComponent('projects').parameters.dataSrcJoinable}` target-src='${value}')
+          orbit-reactivity(data-src=`${getComponent('projects').parameters.post}` target-src='${value}')
           orbit-reactivity(bind-user nested-field="projects" target-src='${value}')
 
       solid-display(
         class='table-body'
         filtered-by="admin-project-filter"
 
-        data-src=`${getComponent('projects').endpoints.get}joinable/`
+        data-src=`${getComponent('projects').parameters.dataSrcJoinable}`
         fields='cell1(customer.name, counter, name), cell2(members), cell3(captain), cell4(joinButton)'
         loader-id='loader-admin-projects'
 
diff --git a/src/views/partials/circle/page-circle-chat.pug b/src/views/partials/circle/page-circle-chat.pug
index 80ad844f..8ba95db0 100644
--- a/src/views/partials/circle/page-circle-chat.pug
+++ b/src/views/partials/circle/page-circle-chat.pug
@@ -16,7 +16,7 @@ div.segment.full.padding-large.sm-padding-top-small.sm-padding-right-xsmall.sm-p
   solid-xmpp-chat(
     data-authentication='login'
     data-auto-login='true'
-    data-websocket-url=component.endpoints.xmpp
+    data-websocket-url=component.parameters.xmpp
     bind-resources
     uniq=component.uniq
   )
diff --git a/src/views/partials/circle/page-circle-edit.pug b/src/views/partials/circle/page-circle-edit.pug
index f3d2ce6f..4804bf66 100644
--- a/src/views/partials/circle/page-circle-edit.pug
+++ b/src/views/partials/circle/page-circle-edit.pug
@@ -33,7 +33,7 @@ div.segment.full.padding-large.whitespace-normal
       required-owner
       required-subtitle
       required-community.community
-      range-owner=`${component.endpoints.owners}`
+      range-owner=`${component.parameters.owners}`
 
       label-name=''
       label-owner=''
@@ -79,7 +79,7 @@ div.segment.full.padding-large.whitespace-normal
       bind-resources 
       nested-field='members'
       fields='user'
-      range-user=`${component.endpoints.users}`
+      range-user=`${component.parameters.users}`
 
       class-user='segment block margin-bottom'
       widget-user='solid-form-dropdown-autocompletion'
diff --git a/src/views/partials/circle/page-circle-events.pug b/src/views/partials/circle/page-circle-events.pug
index d9a88c3f..07e54e95 100644
--- a/src/views/partials/circle/page-circle-events.pug
+++ b/src/views/partials/circle/page-circle-events.pug
@@ -1,9 +1,9 @@
 solid-event(
   bind-resources
   nested-field="events"
-  range-event-type=`${extension.endpoints.typeevents}`
-  range-event-circle=`${getComponent('circles').endpoints.get}/`
-  upload-dir=`${extension.endpoints.uploads}`
+  range-event-type=`${extension.parameters.typeevents}`
+  range-event-circle=`${extension.parameters.circles}`
+  upload-dir=`${extension.parameters.uploads}`
   id-prefix='default'
   uniq=extension.uniq
 )
diff --git a/src/views/partials/circle/page-circle-polls.pug b/src/views/partials/circle/page-circle-polls.pug
index ae4f8d8d..01fec777 100644
--- a/src/views/partials/circle/page-circle-polls.pug
+++ b/src/views/partials/circle/page-circle-polls.pug
@@ -1,10 +1,10 @@
 solid-poll(
   bind-resources
   nested-field="polls"
-  data-dest=extension.endpoints.post
-  range-tags=extension.endpoints.pollRangeTags
-  range-circles=extension.endpoints.pollRangeCircles
-  upload-dir=extension.endpoints.uploads
+  data-dest=extension.parameters.post
+  range-tags=extension.parameters.pollRangeTags
+  range-circles=extension.parameters.pollRangeCircles
+  upload-dir=extension.parameters.uploads
   uniq=extension.uniq
 )&attributes({
   "display-start-end-dates": extension.parameters ? extension.parameters.displayStartEndDates : false
diff --git a/src/views/partials/circle/page-circle-resources.pug b/src/views/partials/circle/page-circle-resources.pug
index 0f0faa2a..b70db1d7 100644
--- a/src/views/partials/circle/page-circle-resources.pug
+++ b/src/views/partials/circle/page-circle-resources.pug
@@ -1,14 +1,14 @@
 solid-resource(
   bind-resources
   nested-field="resources"
-  post-data-src=`${extension.endpoints.post}`
-  range-resource-type=`${extension.endpoints.types}`
-  post-data-type-src=`${extension.endpoints.postTypes}`
-  range-resource-keyword=`${extension.endpoints.keywords}`
-  post-data-keyword-src=`${extension.endpoints.postKeywords}`
-  circles=`${extension.endpoints.circles}/`
+  post-data-src=`${extension.parameters.post}`
+  range-resource-type=`${extension.parameters.types}`
+  post-data-type-src=`${extension.parameters.postTypes}`
+  range-resource-keyword=`${extension.parameters.keywords}`
+  post-data-keyword-src=`${extension.parameters.postKeywords}`
+  circles=`${extension.parameters.circles}`
   associated-circle-label=""
-  upload-dir=`${extension.endpoints.uploads}`
+  upload-dir=`${extension.parameters.uploads}`
   id-prefix='circles'
   uniq=extension.uniq
 )
diff --git a/src/views/partials/communities/page-community-directory.pug b/src/views/partials/communities/page-community-directory.pug
index 0fe8fa59..7b5f0f0d 100644
--- a/src/views/partials/communities/page-community-directory.pug
+++ b/src/views/partials/communities/page-community-directory.pug
@@ -26,7 +26,7 @@
       div
     solid-display(
       class='segment full children children-quarter sm-children-full children-margin-bottom-medium sm-children-margin-bottom-xsmall children-padding-right-xsmall children-padding-left-xsmall sm-children-padding-none sm-whitespace-normal masonry pagination text-disable-selection'
-      data-src=`${component.endpoints.get}`
+      data-src=`${component.parameters.dataSrc}`
       loader-id=`loader-${component.route}-directory`
       fields='segment1(segment2(logo), segment3(name, profile.shortDescription, counter))'
       filtered-by=`communities-filter`
diff --git a/src/views/partials/communities/page-community-edit.pug b/src/views/partials/communities/page-community-edit.pug
index 043ca127..ffa7039a 100644
--- a/src/views/partials/communities/page-community-edit.pug
+++ b/src/views/partials/communities/page-community-edit.pug
@@ -53,7 +53,7 @@ div.bg-color-white
         widget-profile.description="solid-form-richtext-label"
         widget-profile.email="orbit-communities-edit-email"
         widget-profile.website="orbit-communities-edit-website"
-        upload-url-logo=component.endpoints.uploads
+        upload-url-logo=component.parameters.uploads
         widget-logo="solid-form-image-label"
 
         submit-button=""
@@ -70,9 +70,9 @@ div.bg-color-white
 
         fields="profile.picture1, profile.picture2, profile.picture3"
 
-        upload-url-profile.picture1=component.endpoints.uploads
-        upload-url-profile.picture2=component.endpoints.uploads
-        upload-url-profile.picture3=component.endpoints.uploads
+        upload-url-profile.picture1=component.parameters.uploads
+        upload-url-profile.picture2=component.parameters.uploads
+        upload-url-profile.picture3=component.parameters.uploads
 
         widget-profile.picture1="solid-form-image-label"
         widget-profile.picture2="solid-form-image-label"
diff --git a/src/views/partials/communities/page-community-map.pug b/src/views/partials/communities/page-community-map.pug
index 0c5978d3..7a3b85a6 100644
--- a/src/views/partials/communities/page-community-map.pug
+++ b/src/views/partials/communities/page-community-map.pug
@@ -21,7 +21,7 @@
         span
 
   solid-map.communities-map.margin-right-xsmall.margin-left-xsmall.sm-margin-none.shadow(
-      data-src=`${component.endpoints.addresses}`
+      data-src=`${component.parameters.addresses}`
       loader-id=`loader-${component.route}-map`
       fields="position(segment1(community.logo), segment2(community.name, community.profile.shortDescription, community.members))"
 
diff --git a/src/views/partials/project/page-project-chat.pug b/src/views/partials/project/page-project-chat.pug
index 0af86289..85c71a87 100644
--- a/src/views/partials/project/page-project-chat.pug
+++ b/src/views/partials/project/page-project-chat.pug
@@ -20,7 +20,7 @@ div.segment.full.padding-large.sm-padding-top-small.sm-padding-right-xsmall.sm-p
   solid-xmpp-chat(
     data-authentication='login'
     data-auto-login='true'
-    data-websocket-url=component.endpoints.xmpp
+    data-websocket-url=component.parameters.xmpp
     bind-resources
     uniq=component.uniq
   )
diff --git a/src/views/partials/project/page-project-edit.pug b/src/views/partials/project/page-project-edit.pug
index 932794e8..cdb1d8c1 100644
--- a/src/views/partials/project/page-project-edit.pug
+++ b/src/views/partials/project/page-project-edit.pug
@@ -31,7 +31,7 @@ div.segment.full.padding-large.whitespace-normal
     solid-form.form(
       bind-resources 
 
-      range-captain=`${getComponent('projects').endpoints.captains}`
+      range-captain=`${getComponent('projects').parameters.captains}`
       
       required-status
       required-customer.name
@@ -149,7 +149,7 @@ div.segment.full.padding-large.whitespace-normal
       bind-resources 
       nested-field='members'
       fields='user'
-      range-user=`${component.endpoints.users}`
+      range-user=`${component.parameters.users}`
 
       class-user='add-member'
       label-user=''
diff --git a/src/views/partials/project/page-project-invoices.pug b/src/views/partials/project/page-project-invoices.pug
index ee153cc8..8e8a4b22 100644
--- a/src/views/partials/project/page-project-invoices.pug
+++ b/src/views/partials/project/page-project-invoices.pug
@@ -14,7 +14,7 @@ div.segment.full.padding-large.sm-padding-top-small.sm-padding-right-xsmall.sm-p
   )
 solid-invoicing(
   bind-resources
-  upload-dir=`${extension.endpoints.uploads}`
+  upload-dir=`${extension.parameters.uploads}`
   logo-dir=`${client.logo}`
   uniq=extension.uniq
 )
\ No newline at end of file
diff --git a/src/views/partials/project/page-project-picture.pug b/src/views/partials/project/page-project-picture.pug
index e39a10d6..d081ef1d 100644
--- a/src/views/partials/project/page-project-picture.pug
+++ b/src/views/partials/project/page-project-picture.pug
@@ -6,7 +6,7 @@
 
 //-   solid-picture.project-edit-picture(
 //-     bind-resources
-//-     upload-src=`${component.endpoints.uploads}`
+//-     upload-src=`${component.parameters.uploads}`
 //-     upload-id="solid-project-edit-picture"
 //-     nested-fields='customer'
 //-     fields='logo'
-- 
GitLab


From 43eb878ae602109906c0cadf76d2588dcd023647 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Pasquier <contact@jbpasquier.eu>
Date: Tue, 15 Jun 2021 22:01:52 +0200
Subject: [PATCH 02/18] fix: new sample

---
 config.sample.json | 70 +++++++++++++++++++++++++++-------------------
 1 file changed, 42 insertions(+), 28 deletions(-)

diff --git a/config.sample.json b/config.sample.json
index b8e2bd19..3a7a5bab 100644
--- a/config.sample.json
+++ b/config.sample.json
@@ -1,17 +1,16 @@
 {
   "client": {
     "name": "Sample of a functional Orbit",
-    "logo": "https://cdn.startinblox.com/logos/webp/startinblox.webp"
+    "logo": "https://cdn.startinblox.com/logos/webp/startinblox.webp",
+    "server": "http://localhost:8000"
   },
   "components": [{
       "type": "registering",
       "parameters": {
+        "dataSrc": "server://open-communities/",
         "authority": "http://localhost:8000/",
         "authorityName": "dataserver"
       },
-      "endpoints": {
-        "get": "http://localhost:8000/open-communities/"
-      },
       "route": false
     },
     {
@@ -31,51 +30,66 @@
     },
     {
       "type": "dashboard",
-      "endpoints": {
-        "get": "http://localhost:8000/dashboards/"
-      }
+      "parameters": {
+        "dataSrc": "server://dashboards/"
+      },
+      "experimental": [
+        "routing"
+      ]
     },
     {
       "type": "profileDirectory",
-      "endpoints": {
-        "get": "http://localhost:8000/users/",
-        "skills": "http://localhost:8000/skills/",
-        "uploads": "http://localhost:8000/upload/"
+      "parameters": {
+        "dataSrc": "federation://users/",
+        "rangeSkills": "federation://skills/",
+        "paginateBy": "30",
+        "uploads": "server://upload/"
       },
-      "route": "members"
+      "route": "members",
+      "experimental": [
+        "routing"
+      ]
     },
     {
       "type": "jobBoard",
-      "endpoints": {
-        "get": "http://localhost:8000/job-offers/",
-        "post": "http://localhost:8000/job-offers/",
-        "skills": "http://localhost:8000/skills/"
+      "parameters": {
+        "dataSrc": "federation://job-offers/current/",
+        "dataSrcExpired": "federation://job-offers/expired/",
+        "postDataSrc": "server://job-offers/",
+        "rangeSkills": "federation://skills/"
       },
-      "route": "job-offers"
+      "route": "job-offers",
+      "experimental": [
+        "routing"
+      ]
     },
     {
       "type": "projects",
-      "endpoints": {
-        "get": "http://localhost:8000/projects/",
-        "post": "http://localhost:8000/projects/",
-        "captains": "http://localhost:8000/users/",
-        "users": "http://localhost:8000/users/",
+      "parameters": {
+        "captains": "federation://users/",
+        "circles": "federation://circles/",
+        "dataSrc": "federation://projects/",
+        "dataSrcJoinable": "federation://projects/joinable/",
+        "post": "server://projects/",
+        "users": "federation://users/",
+        "noRender": "",
         "xmpp": "wss://xmpp-dev.startinblox.com/xmpp-websocket"
       }
     },
     {
       "type": "circles",
-      "endpoints": {
-        "get": "http://localhost:8000/circles/",
-        "post": "http://localhost:8000/circles/",
-        "owners": "http://localhost:8000/users/",
-        "users": "http://localhost:8000/users/",
+      "parameters": {
+        "dataSrc": "federation://circles/",
+        "dataSrcJoinable": "federation://circles/joinable/",
+        "owners": "federation://users/",
+        "post": "server://circles/",
+        "users": "federation://users/",
         "xmpp": "wss://xmpp-dev.startinblox.com/xmpp-websocket"
       }
     },
     {
       "type": "chat",
-      "endpoints": {
+      "parameters": {
         "xmpp": "wss://xmpp-dev.startinblox.com/xmpp-websocket"
       },
       "route": "messages"
-- 
GitLab


From b37ed8cf5b9ba1f39a272b75a57cc3cddeab009b Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Pasquier <contact@jbpasquier.eu>
Date: Tue, 15 Jun 2021 22:41:17 +0200
Subject: [PATCH 03/18] fix: experimental routing directory & job board

---
 README.md                                       | 12 ++++++------
 config.sample.json                              |  4 ++--
 src/index.pug                                   | 10 +++++-----
 src/orbit-dependencies.pug                      |  4 ++--
 src/views/page-messages.pug                     |  2 +-
 src/views/page-profile.pug                      |  6 +++---
 src/views/partials/header.pug                   |  8 ++++----
 src/views/partials/menu-left.pug                |  8 ++++----
 src/views/partials/widgets/orbit-menu-empty.pug |  2 +-
 9 files changed, 28 insertions(+), 28 deletions(-)

diff --git a/README.md b/README.md
index 8230bfeb..beea84ad 100644
--- a/README.md
+++ b/README.md
@@ -509,7 +509,7 @@ Module declaration, on `config.json`:
 
 ```json
     {
-      "type": "jobBoard",
+      "type": "job-board",
       "parameters": {
         "dataSrc": "federation://job-offers/current/",
         "dataSrcExpired": "federation://job-offers/expired/",
@@ -714,7 +714,7 @@ Module declaration, on `config.json`:
 
 ```json
     {
-      "type": "profileDirectory",
+      "type": "directory",
       "parameters": {
         "dataSrc": "federation://users/",
         "rangeSkills": "federation://skills/",
@@ -742,17 +742,17 @@ Eg. for the Users Directory:
 
 ```json
     {
-      "type": "profileDirectory",
+      "type": "directory",
       "parameters": {
         "dataSrc": "server://users/",
         "skills": "server://skills/",
         "uploads": "server://upload/"
       },
-      "route": "directory"
+      "route": "profiles"
     }
 ```
 
-Will lead to `http://client.url/directory` as URL to reach the module instead of the default `http://client.url/profileDirectory`.
+Will lead to `http://client.url/profiles` as URL to reach the module instead of the default `http://client.url/directory`.
 
 If you provide no `route`, then Orbit will use the `type` as route view name. If two modules share the same `route`, they'll get suffixed with a random unique id.
 
@@ -793,7 +793,7 @@ Eg.:
 
 ```json
     {
-      "type": "profileDirectory",
+      "type": "directory",
       "parameters": {
         "dataSrc": "server://users/",
         "skills": "server://skills/",
diff --git a/config.sample.json b/config.sample.json
index 3a7a5bab..7d9c843a 100644
--- a/config.sample.json
+++ b/config.sample.json
@@ -38,7 +38,7 @@
       ]
     },
     {
-      "type": "profileDirectory",
+      "type": "directory",
       "parameters": {
         "dataSrc": "federation://users/",
         "rangeSkills": "federation://skills/",
@@ -51,7 +51,7 @@
       ]
     },
     {
-      "type": "jobBoard",
+      "type": "job-board",
       "parameters": {
         "dataSrc": "federation://job-offers/current/",
         "dataSrcExpired": "federation://job-offers/expired/",
diff --git a/src/index.pug b/src/index.pug
index 969592e5..8c3054b1 100644
--- a/src/index.pug
+++ b/src/index.pug
@@ -134,7 +134,7 @@ html(lang="en")
                 .scrollbar-content.bg-color-white
                   include views/page-events.pug
 
-              if component.type == "jobBoard"
+              if component.type == "job-board"
                 orbit-reactivity(data-src=`${component.parameters.dataSrcExpired}` target-src=`${component.parameters.dataSrc}`)
                 orbit-reactivity(data-src=`${component.parameters.post}` target-src=`${component.parameters.dataSrc}`)
                 orbit-reactivity(data-src=`${component.parameters.dataSrcExpired}` target-src=`${component.parameters.post}`)
@@ -146,7 +146,7 @@ html(lang="en")
                 .scrollbar-content
                   include views/page-polls.pug
 
-              if component.type == "profileDirectory"
+              if component.type == "directory"
                 orbit-reactivity(bind-user nested-field="profile" target-src="store://user")
                 orbit-reactivity(bind-user nested-field="account" target-src="store://user")
 
@@ -190,11 +190,11 @@ html(lang="en")
                 file=component.parameters.file
               )
 
-        if componentSet.has('profileDirectory')
+        if componentSet.has('directory')
           .scrollbar-content(
-            id=`${getRoute("profileDirectory", true)}-profile`
+            id=`${getRoute("directory", true)}-profile`
             hidden
-            data-view=`${getRoute("profileDirectory", true)}-profile`
+            data-view=`${getRoute("directory", true)}-profile`
             no-render
           )
             include views/page-profile.pug
diff --git a/src/orbit-dependencies.pug b/src/orbit-dependencies.pug
index 9c9295a7..b3e68642 100644
--- a/src/orbit-dependencies.pug
+++ b/src/orbit-dependencies.pug
@@ -39,7 +39,7 @@ if componentSet.has('invoices')
   script(type="module" src="https://cdn.skypack.dev/@startinblox/component-invoicing@1.4" defer)
   //- script(type="module" src="/lib/solid-invoicing/solid-invoicing.js" defer)
 
-if componentSet.has("jobBoard")
+if componentSet.has("job-board")
   script(type="module" src="https://cdn.skypack.dev/@startinblox/component-job-board@6.2" defer)
   //- script(type="module" src="/lib/solid-job-board/dist/index.js" defer)
 
@@ -51,7 +51,7 @@ if componentSet.has("polls")
   script(type="module" src="https://cdn.skypack.dev/@startinblox/component-poll@3.1" defer)
   //- script(type="module" src="/lib/solid-poll/index.js" defer)
 
-if componentSet.has("profileDirectory")
+if componentSet.has("directory")
   script(type="module" src="https://cdn.skypack.dev/@startinblox/component-directory@6.1" defer)
   //- script(type="module" src="/lib/solid-directory/dist/index.js" defer)
 
diff --git a/src/views/page-messages.pug b/src/views/page-messages.pug
index e5e91226..40ee9479 100644
--- a/src/views/page-messages.pug
+++ b/src/views/page-messages.pug
@@ -18,7 +18,7 @@ div.segment.full.padding-top-small.padding-right-large.padding-bottom-small.padd
 
   solid-link.icon.icon-info.icon-secondary.hover(
     bind-resources
-    next=`${getRoute('profileDirectory', true)}-member-profile`
+    next=`${getRoute('directory', true)}-member-profile`
   )
 
 .chat-view.segment.full.whitespace-normal
diff --git a/src/views/page-profile.pug b/src/views/page-profile.pug
index ae4673f8..8a48717e 100644
--- a/src/views/page-profile.pug
+++ b/src/views/page-profile.pug
@@ -1,6 +1,6 @@
 solid-profile(
   bind-user
-  upload-src=`${getComponent('profileDirectory', true).parameters.uploads}`
-  range-skills=`${getComponent('profileDirectory', true).parameters.skills}`
-  uniq=`${getComponent('profileDirectory', true).uniq}`
+  upload-src=`${getComponent('directory', true).parameters.uploads}`
+  range-skills=`${getComponent('directory', true).parameters.skills}`
+  uniq=`${getComponent('directory', true).uniq}`
 )
diff --git a/src/views/partials/header.pug b/src/views/partials/header.pug
index 2b5fb284..1d3e7bc4 100644
--- a/src/views/partials/header.pug
+++ b/src/views/partials/header.pug
@@ -38,9 +38,9 @@ div
         div.panel
           nav.bg-color-white.text-semibold.text-color-heading
             ul
-              if componentSet.has("profileDirectory")
+              if componentSet.has("directory")
                 li.border-bottom.border-color-grey
-                  solid-link.segment.padding-small.text-hover(next=`${getRoute("profileDirectory", true)}-profile` data-trans='header.myProfile')
+                  solid-link.segment.padding-small.text-hover(next=`${getRoute("directory", true)}-profile` data-trans='header.myProfile')
               if componentSet.has("admin")
                 li.segment.padding-small.border-bottom.border-color-grey
                   div(data-trans='header.admin')
@@ -89,9 +89,9 @@ div
         div.panel
           nav.bg-color-white.text-semibold.text-color-heading
             ul
-              if componentSet.has("profileDirectory")
+              if componentSet.has("directory")
                 li.border-bottom.border-color-grey
-                  solid-link.segment.padding-small.sm-padding-medium.sm-padding-left-xlarge.text-hover(next=`${getRoute("profileDirectory", true)}-profile` data-trans='header.myProfile')
+                  solid-link.segment.padding-small.sm-padding-medium.sm-padding-left-xlarge.text-hover(next=`${getRoute("directory", true)}-profile` data-trans='header.myProfile')
               if componentSet.has("admin")
                 li.segment.padding-small.sm-padding-medium.sm-padding-left-xlarge.border-bottom.border-color-grey
                   div(data-trans='header.admin')
diff --git a/src/views/partials/menu-left.pug b/src/views/partials/menu-left.pug
index 926b72cb..bb40e842 100644
--- a/src/views/partials/menu-left.pug
+++ b/src/views/partials/menu-left.pug
@@ -76,9 +76,9 @@ solid-router#navbar-router(default-route=defaultRoute)
             div.segment.margin-right-xxsmall.jsMenuArrow
               div.segment.icon.icon-small.icon-arrow-down
             div.segment.half.text-uppercase.text-letter-spacing-large(data-trans=`${component.name?component.name:"menuLeft.messages"}`)
-            if componentSet.has("profileDirectory")
+            if componentSet.has("directory")
               div.menu-options.segment.jsMenuOption
-                solid-link.segment.block.menu-icon.icon.icon-small.icon-user-follow(next=getRoute('profileDirectory', true))
+                solid-link.segment.block.menu-icon.icon.icon-small.icon-user-follow(next=getRoute('directory', true))
             solid-badge.badge(data-type="Message")
           solid-route(name=`admin-${component.route}`, hidden)
           solid-route(name=`admin-${component.route}-create` use-id='' hidden)
@@ -137,14 +137,14 @@ solid-router#navbar-router(default-route=defaultRoute)
 
             )
         div.divider
-      if component.type == "profileDirectory"
+      if component.type == "directory"
         solid-route.menu-tab.segment.full.padding-xsmall.text-semibold.text-color-white.heading-active.bg-color-heading.transparent-background.hover.active(name=component.route)
           div.segment.margin-right-xxsmall
             div.icon.icon-small.icon-people
           div.segment.text-uppercase.text-letter-spacing-large(data-trans="menuLeft.profileDirectory")
         solid-route.menu(name=`${component.route}-profile`, hidden)
         div.divider
-      if component.type == "jobBoard"
+      if component.type == "job-board"
         solid-route.menu-tab.segment.full.padding-xsmall.text-semibold.text-color-white.heading-active.bg-color-heading.transparent-background.hover.active(name=component.route, rdf-type='hd:joboffer')
           div.segment.margin-right-xxsmall
             div.segment.icon.icon-small.icon-briefcase
diff --git a/src/views/partials/widgets/orbit-menu-empty.pug b/src/views/partials/widgets/orbit-menu-empty.pug
index ed14b9dc..1a888387 100644
--- a/src/views/partials/widgets/orbit-menu-empty.pug
+++ b/src/views/partials/widgets/orbit-menu-empty.pug
@@ -1,4 +1,4 @@
-if componentSet.has('circles') || componentSet.has('projects') || componentSet.has('profileDirectory') || componentSet.has('chat')
+if componentSet.has('circles') || componentSet.has('projects') || componentSet.has('directory') || componentSet.has('chat')
   solid-widget(name='orbit-menu-empty')
     template
       p.segment.full.empty.whitespace-normal(style='display:block!important; color:#D0D4DA')
-- 
GitLab


From f471722f97b6907688bc33972b268e8e2eeaf041 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Pasquier <contact@jbpasquier.eu>
Date: Wed, 16 Jun 2021 15:41:32 +0200
Subject: [PATCH 04/18] patch: @cache false

---
 src/orbit-envoy.pug | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/orbit-envoy.pug b/src/orbit-envoy.pug
index dc95b44e..fa3c25de 100644
--- a/src/orbit-envoy.pug
+++ b/src/orbit-envoy.pug
@@ -114,6 +114,7 @@ for component of components
         if(typeof path === 'string') {
           if(path.startsWith('federation://')) {
             federations[`store://local.${component.uniq}/${attribute}/`] = {
+              "@cache": "false",
               "@context": "https://cdn.happy-dev.fr/owl/hdcontext.jsonld",
               "@type": "ldp:Container",
               "@id": `store://local.${component.uniq}/${attribute}/`,
@@ -163,6 +164,7 @@ for component of components
             if(typeof path === 'string') {
               if(path.startsWith('federation://')) {
                 federations[`store://local.${extension.uniq}/${attribute}/`] = {
+                  "@cache": "false",
                   "@context": "https://cdn.happy-dev.fr/owl/hdcontext.jsonld",
                   "@type": "ldp:Container",
                   "@id": `store://local.${extension.uniq}/${attribute}/`,
-- 
GitLab


From 8d85f102b6b71c85c6917efa8a56ece3fdb1a447 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Pasquier <contact@jbpasquier.eu>
Date: Fri, 18 Jun 2021 16:47:43 +0200
Subject: [PATCH 05/18] fix: hubl-workaround-493

---
 src/views/partials/admin/page-admin-circles-join.pug    | 2 +-
 src/views/partials/admin/page-admin-projects.pug        | 2 +-
 src/views/partials/widgets/orbit-circle-join-button.pug | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/views/partials/admin/page-admin-circles-join.pug b/src/views/partials/admin/page-admin-circles-join.pug
index 7f8025f3..528be8ad 100644
--- a/src/views/partials/admin/page-admin-circles-join.pug
+++ b/src/views/partials/admin/page-admin-circles-join.pug
@@ -5,7 +5,7 @@ solid-widget(name=`orbit-admin-circle-join-button`)
       data-src='${value}'
 
       fields='user.username'
-      value-user.username='orbit-workaround-493'
+      value-user.username='hubl-workaround-493'
       widget-user.username='solid-form-hidden'
 
       submit-button=''
diff --git a/src/views/partials/admin/page-admin-projects.pug b/src/views/partials/admin/page-admin-projects.pug
index 32c121e6..debfd69e 100644
--- a/src/views/partials/admin/page-admin-projects.pug
+++ b/src/views/partials/admin/page-admin-projects.pug
@@ -109,7 +109,7 @@ div.segment.full.padding-large.sm-padding-xsmall.sm-padding-top-medium.whitespac
             nested-field="members"
 
             fields='user.username'
-            value-user.username='orbit-workaround-493'
+            value-user.username='hubl-workaround-493'
             widget-user.username='solid-form-hidden'
 
             submit-button=''
diff --git a/src/views/partials/widgets/orbit-circle-join-button.pug b/src/views/partials/widgets/orbit-circle-join-button.pug
index 2b3c4cd7..9afba962 100644
--- a/src/views/partials/widgets/orbit-circle-join-button.pug
+++ b/src/views/partials/widgets/orbit-circle-join-button.pug
@@ -7,7 +7,7 @@ if componentSet.has('circles')
           nested-field='members'
 
           fields='user.username'
-          value-user.username='orbit-workaround-493'
+          value-user.username='hubl-workaround-493'
           widget-user.username='solid-form-hidden'
 
           submit-button=''
-- 
GitLab


From 60d719fa6a3f5e9f185a7fe4e90458615718bd93 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Pasquier <contact@jbpasquier.eu>
Date: Fri, 18 Jun 2021 15:37:19 +0000
Subject: [PATCH 06/18] fix: missing class on events

---
 src/index.pug | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/index.pug b/src/index.pug
index 8c3054b1..1ac36057 100644
--- a/src/index.pug
+++ b/src/index.pug
@@ -131,7 +131,7 @@ html(lang="en")
                   include views/page-communities.pug
 
               if component.type == "events"
-                .scrollbar-content.bg-color-white
+                .scrollbar-content.whitespace-normal.bg-color-white
                   include views/page-events.pug
 
               if component.type == "job-board"
-- 
GitLab


From 77a1d2bd4d87e49dac1b0af6509c3f55fec74a0b Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Pasquier <contact@jbpasquier.eu>
Date: Tue, 22 Jun 2021 12:39:21 +0200
Subject: [PATCH 07/18] feature: allow external link on menu

---
 README.md                        | 19 +++++++++++++++++++
 src/views/partials/menu-left.pug |  6 ++++++
 2 files changed, 25 insertions(+)

diff --git a/README.md b/README.md
index beea84ad..5ef74a3c 100644
--- a/README.md
+++ b/README.md
@@ -734,6 +734,25 @@ Module declaration, on `config.json`:
 
 You need the experimental routing enabled to have a Profile Directory.
 
+### Add link to left menu
+
+```
+  {
+    "type": "link",
+    "parameters": {
+      "icon": "icon-docs",
+      "name": "some.path",
+      "target": "https://startinblox.com"
+    },
+    "route": true
+  },
+```
+
+Where:
+* `parameters.icon` is a valid simple line icon
+* `parameters.name` is a path to a translation. You can create your own with the lang component
+* `parameters.target` is the target URL
+
 ### Route generation
 
 Orbit will, by default, generate an unique route for every of your module. You can customize this route by setting a `route` attribute on your module declaration.
diff --git a/src/views/partials/menu-left.pug b/src/views/partials/menu-left.pug
index bb40e842..2fd8c5ae 100644
--- a/src/views/partials/menu-left.pug
+++ b/src/views/partials/menu-left.pug
@@ -168,6 +168,12 @@ solid-router#navbar-router(default-route=defaultRoute)
             div.segment.icon.icon-small.icon-calendar
           div.segment.text-uppercase.text-letter-spacing-large(data-trans="menuLeft.events")
         div.divider
+      if component.type == "link"
+        a.menu-tab.segment.full.padding-xsmall.text-semibold.text-color-white.heading-active.bg-color-heading.transparent-background.hover.active(href=component.parameters.target, target="_blank")
+          div.segment.margin-right-xxsmall
+            div.segment.icon.icon-small&attributes({"class": component.parameters.icon})
+          div.segment.text-uppercase.text-letter-spacing-large(data-trans=component.parameters.name)
+        div.divider
   if componentSet.has('registering')
     solid-route(name='join-community', use-id, hidden)
     solid-route(name='login', hidden)
-- 
GitLab


From 2ed2aca8252bc510673266e3d1c880fcdc16b0bb Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Pasquier <contact@jbpasquier.eu>
Date: Tue, 22 Jun 2021 13:32:19 +0000
Subject: [PATCH 08/18] fix: wrong route

---
 src/views/page-profile.pug | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/views/page-profile.pug b/src/views/page-profile.pug
index 8a48717e..8fe5738d 100644
--- a/src/views/page-profile.pug
+++ b/src/views/page-profile.pug
@@ -1,6 +1,6 @@
 solid-profile(
   bind-user
   upload-src=`${getComponent('directory', true).parameters.uploads}`
-  range-skills=`${getComponent('directory', true).parameters.skills}`
+  range-skills=`${getComponent('directory', true).parameters.rangeSkills}`
   uniq=`${getComponent('directory', true).uniq}`
 )
-- 
GitLab


From dda612c521e4884c712d493570c9eaa52548b1fa Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Pasquier <contact@jbpasquier.eu>
Date: Tue, 22 Jun 2021 15:56:06 +0200
Subject: [PATCH 09/18] fix: dependencies

---
 package-lock.json | 11 +++--------
 package.json      |  3 +--
 2 files changed, 4 insertions(+), 10 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 49a42d70..ef41bdbd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1615,15 +1615,10 @@
         "physical-cpu-count": "^2.0.0"
       }
     },
-    "@startinblox/hubl-styling-framework": {
-      "version": "1.9.8",
-      "resolved": "https://registry.npmjs.org/@startinblox/hubl-styling-framework/-/hubl-styling-framework-1.9.8.tgz",
-      "integrity": "sha512-MLKE/4sr74yETmWEgLi7vOLyUiF8znXzqsr8qh7qRuInh3nhm2b09KmvLMY1FY/qTBc/dXP0gH4aibI2BRwFLA=="
-    },
     "@startinblox/orbit-styling-framework": {
-      "version": "1.9.9",
-      "resolved": "https://registry.npmjs.org/@startinblox/orbit-styling-framework/-/orbit-styling-framework-1.9.9.tgz",
-      "integrity": "sha512-Y57drIGwsKwEHUOvoZEuJGP9mW58r0BBHgAaJZfzbFXfAKGEu9U2fPBFzQneApDBotBo+6IWmrfCTQz3uo4weQ=="
+      "version": "1.9.10",
+      "resolved": "https://registry.npmjs.org/@startinblox/orbit-styling-framework/-/orbit-styling-framework-1.9.10.tgz",
+      "integrity": "sha512-cRo8xUhIZqk4Op+2RjnR7NIc2NZaGhIQRt90LkhnroNJk4D7SwEgA5bZfk/EgCOO/UVRRb0Rm7Nx2wW03AMOiw=="
     },
     "@types/node": {
       "version": "14.17.3",
diff --git a/package.json b/package.json
index 03ec5759..bd886c27 100644
--- a/package.json
+++ b/package.json
@@ -62,8 +62,7 @@
     }
   },
   "dependencies": {
-    "@startinblox/hubl-styling-framework": "^1.9.8",
-    "@startinblox/orbit-styling-framework": "^1.9.9",
+    "@startinblox/orbit-styling-framework": "^1.9.10",
     "autoprefixer": "^9.8.6",
     "cross-env": "^7.0.3",
     "fs-extra": "^10.0.0",
-- 
GitLab


From 239eb2224bb1a3ead9c87a752c50e7c808a32c66 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Pasquier <contact@jbpasquier.eu>
Date: Wed, 23 Jun 2021 13:35:16 +0200
Subject: [PATCH 10/18] fix: secoia intl

---
 src/scripts/intl.js | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/scripts/intl.js b/src/scripts/intl.js
index 16197e45..21339310 100644
--- a/src/scripts/intl.js
+++ b/src/scripts/intl.js
@@ -257,7 +257,7 @@ window.orbit.intl = new JsI18n;
 document.addEventListener("DOMContentLoaded", () => {
   // Detect the lang & initialize, based on the browser or "language" item from localstorage
   window.orbit.intl.detectLanguage();
-  let timer;
+  let timer, timer2;
   (new MutationObserver((mutations) => {
     mutations.forEach(mutation => {
       if (mutation.target.attributes["data-trans"] != null) {
@@ -267,6 +267,10 @@ document.addEventListener("DOMContentLoaded", () => {
         clearTimeout(timer);
         timer = setTimeout(() => window.orbit.intl.processNode(document.querySelector('body')), 500);
       }
+      // Hotfix for Secoia
+      if (mutation.target.nodeName == "SOLID-DIRECTORY") {
+        if(!timer2) timer2 = setTimeout(() => mutation.target.render(), 500);
+      }
     });
   }).observe(document.body, {
     subtree: true,
-- 
GitLab


From 403f35e6bf3665f107936be6e2e657a4c791e019 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Pasquier <contact@jbpasquier.eu>
Date: Wed, 23 Jun 2021 13:46:22 +0200
Subject: [PATCH 11/18] increase timer for secoia

---
 src/scripts/intl.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/scripts/intl.js b/src/scripts/intl.js
index 21339310..52148697 100644
--- a/src/scripts/intl.js
+++ b/src/scripts/intl.js
@@ -269,7 +269,7 @@ document.addEventListener("DOMContentLoaded", () => {
       }
       // Hotfix for Secoia
       if (mutation.target.nodeName == "SOLID-DIRECTORY") {
-        if(!timer2) timer2 = setTimeout(() => mutation.target.render(), 500);
+        if(!timer2) timer2 = setTimeout(() => mutation.target.render(), 1500);
       }
     });
   }).observe(document.body, {
-- 
GitLab


From 74b8de9ac907b442ca900276d4fadfa2de525601 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Pasquier <contact@jbpasquier.eu>
Date: Tue, 29 Jun 2021 11:24:35 +0000
Subject: [PATCH 12/18] fix: secoia timeout

---
 src/scripts/intl.js | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/scripts/intl.js b/src/scripts/intl.js
index 52148697..ee503976 100644
--- a/src/scripts/intl.js
+++ b/src/scripts/intl.js
@@ -269,7 +269,10 @@ document.addEventListener("DOMContentLoaded", () => {
       }
       // Hotfix for Secoia
       if (mutation.target.nodeName == "SOLID-DIRECTORY") {
-        if(!timer2) timer2 = setTimeout(() => mutation.target.render(), 1500);
+        if(!timer2) {
+          setTimeout(() => mutation.target.render(), 1000);
+          timer2 = setTimeout(() => mutation.target.render(), 2000);
+        }
       }
     });
   }).observe(document.body, {
-- 
GitLab


From cf1add07cbe0ca4df89067a18f34c9796c982795 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Pasquier <contact@jbpasquier.eu>
Date: Mon, 5 Jul 2021 18:05:23 +0200
Subject: [PATCH 13/18] minor: dynamic npm imports

---
 README.md                          |  37 +++++++-
 internal/assets.js                 |   4 +-
 internal/parcel.js                 |   3 -
 src/components/orbit-auto-login.js |  24 -----
 src/components/orbit-reactivity.js |  54 -----------
 src/components/sw-toolbox.js       |  47 ----------
 src/index.pug                      |   3 -
 src/libs/sw-toolbox.js             |  44 +++++++++
 src/orbit-dependencies.pug         | 138 ++++++++++++++++++++---------
 src/scripts/orbit-auto-login.js    |  21 +++++
 src/scripts/orbit-reactivity.js    |  49 ++++++++++
 11 files changed, 248 insertions(+), 176 deletions(-)
 delete mode 100644 src/components/orbit-auto-login.js
 delete mode 100644 src/components/orbit-reactivity.js
 delete mode 100644 src/components/sw-toolbox.js
 create mode 100644 src/libs/sw-toolbox.js
 create mode 100644 src/scripts/orbit-auto-login.js
 create mode 100644 src/scripts/orbit-reactivity.js

diff --git a/README.md b/README.md
index 5ef74a3c..c84421b1 100644
--- a/README.md
+++ b/README.md
@@ -494,7 +494,6 @@ Module declaration, on `config.json`:
     }
 ```
 
-
 ### Job Offers
 
 Job Offers includes a job board with conversation. To activate them
@@ -736,7 +735,7 @@ You need the experimental routing enabled to have a Profile Directory.
 
 ### Add link to left menu
 
-```
+```json
   {
     "type": "link",
     "parameters": {
@@ -749,10 +748,44 @@ You need the experimental routing enabled to have a Profile Directory.
 ```
 
 Where:
+
 * `parameters.icon` is a valid simple line icon
 * `parameters.name` is a path to a translation. You can create your own with the lang component
 * `parameters.target` is the target URL
 
+### Enfore NPM package version
+
+To enforce a version to get used from NPM, add this to your `config.json`:
+
+```json
+  "npm": [
+    {
+      "package": "@startinblox/core",
+      "version": "0"
+    }
+  ],
+```
+
+Where:
+
+* `npm[].package` is the package name to include
+* `npm[].version` if the version of the package you want to use
+* `npm[].attributes` (optional) allows to add attributes to the script element
+
+Package will get required from Skypack CDNs.
+
+#### Use local packages
+
+Use the `npm` parameter on your `config.json`
+
+```json
+    {
+      "package": "@startinblox/oidc",
+      "version": "0",
+      "path": "./path/to/sib-auth/sib-auth.js"
+    }
+```
+
 ### Route generation
 
 Orbit will, by default, generate an unique route for every of your module. You can customize this route by setting a `route` attribute on your module declaration.
diff --git a/internal/assets.js b/internal/assets.js
index 1c239021..a3c564b4 100644
--- a/internal/assets.js
+++ b/internal/assets.js
@@ -1,8 +1,8 @@
 const HTMLAsset = require('parcel-bundler/lib/assets/HTMLAsset')
 
 function shouldIgnore (file) {
-  // Ignore img(src="${...}") on pug & keep the components folder pristine
-  return /\${.+}/.test(file) || /components/.test(file) || /\/lib\/solid-/.test(file) || /\/lib\/sib-/.test(file);
+  // Ignore img(src="${...}") on pug
+  return /\${.+}/.test(file)
 }
 
 class SkipStartinbloxWidgetAsset extends HTMLAsset {
diff --git a/internal/parcel.js b/internal/parcel.js
index 170ee85f..edecff24 100644
--- a/internal/parcel.js
+++ b/internal/parcel.js
@@ -63,9 +63,6 @@ const options = {
   await fse.copy("./src/locales", "./dist/locales")
   console.log(`Copied locales to dist folder`);
 
-  await fse.copy("./src/components", "./dist/components")
-  console.log(`Copied components to dist folder`);
-
   const bundler = new Bundler('./src/index.pug', options);
   bundler.addAssetType('html', require.resolve('./assets.js'));
   if (process.env.NODE_ENV !== 'production') {
diff --git a/src/components/orbit-auto-login.js b/src/components/orbit-auto-login.js
deleted file mode 100644
index c6373884..00000000
--- a/src/components/orbit-auto-login.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import {
-  Sib
-} from 'https://cdn.skypack.dev/@startinblox/core@0.17';
-
-export const OrbitAutoLogin = {
-  name: 'orbit-auto-login',
-  created() {
-    if (window.location.pathname == "/login") {
-      document
-        .querySelectorAll(".loggedIn-loader")
-        .forEach(el => (el.style.display = "flex"));
-      window.dispatchEvent(
-        new CustomEvent('requestNavigation', {
-          detail: {
-            route: window.orbit.getRoute((window.orbit.defaultRoute || "dashboard"), true)
-          }
-        }),
-      );
-      document.querySelector("sib-auth").login();
-    }
-  }
-}
-
-Sib.register(OrbitAutoLogin);
\ No newline at end of file
diff --git a/src/components/orbit-reactivity.js b/src/components/orbit-reactivity.js
deleted file mode 100644
index 5b77308d..00000000
--- a/src/components/orbit-reactivity.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import {
-  store,
-  Sib,
-  StoreMixin
-} from 'https://cdn.skypack.dev/@startinblox/core@0.17';
-
-export const OrbitReactivity = {
-  name: 'orbit-reactivity',
-  use: [StoreMixin],
-  attributes: {
-    targetSrc: {
-      type: String,
-      default: '',
-      callback: function () {
-        this.subscribe();
-      }
-    }
-  },
-  async fetchData(value) {
-    this.resourceId = null;
-    if (this.nestedField) {
-      const resource = store.get(value) || await store.getData(value, this.context);
-      const nestedResource = await resource[this.nestedField]
-      this.resourceId = nestedResource ? nestedResource['@id'] : null;
-    } else {
-      this.resourceId = value;
-    }
-    this.subscribe();
-  },
-  unsubscribe(resourceId, targetSrc) {
-    const resourcesSub = store.subscriptionVirtualContainersIndex.get(resourceId);
-    const targetSub = store.subscriptionVirtualContainersIndex.get(targetSrc);
-    const newResourceSub = resourcesSub.filter(r => r !== targetSrc);
-    const newTargetSub = targetSub.filter(r => r !== resourceId);
-    store.subscriptionVirtualContainersIndex.set(resourceId, newResourceSub);
-    store.subscriptionVirtualContainersIndex.set(targetSrc, newTargetSub);
-  },
-  detached() {
-    this.unsubscribe(this.resourceId, this.targetSrc)
-  },
-  subscribe() {
-    if (this.oldResourceId && this.oldTargetSrc && (this.oldResourceId !== this.resourceId || this.oldTargetSrc !== this.targetSrc)) {
-      this.unsubscribe(this.oldResourceId, this.oldTargetSrc);
-    }
-    if (this.resourceId && this.targetSrc) {
-      store.subscribeVirtualContainerTo(this.resourceId, this.targetSrc);
-      store.subscribeVirtualContainerTo(this.targetSrc, this.resourceId);
-      this.oldResourceId = this.resourceId;
-      this.oldTargetSrc = this.targetSrc;
-    }
-  }
-}
-
-Sib.register(OrbitReactivity);
\ No newline at end of file
diff --git a/src/components/sw-toolbox.js b/src/components/sw-toolbox.js
deleted file mode 100644
index 95351579..00000000
--- a/src/components/sw-toolbox.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import {
-  Workbox,
-  messageSW
-} from 'https://storage.googleapis.com/workbox-cdn/releases/6.1.1/workbox-window.prod.mjs';
-
-if ('serviceWorker' in navigator) {
-  window.addEventListener('load', function () {
-
-    const wb = new Workbox('/service-worker.js');
-    let registration;
-
-    const showSkipWaitingPrompt = (event) => {
-      if (orbit.intl.t('serviceWorker.newUpdate') != undefined) {
-        Swal.fire({
-          position: 'bottom-end',
-          backdrop: false,
-          title: "",
-          text: orbit.intl.t('serviceWorker.newUpdate') + ". " + orbit.intl.t('serviceWorker.wantToUpdate'),
-          imageUrl: orbit.client.logo || 'https://cdn.startinblox.com/logos/webp/startinblox.webp',
-          imageAlt: orbit.client.name,
-          showCancelButton: true,
-          confirmButtonClass: 'button text-xsmall text-bold text-center reversed color-secondary bordered icon icon-check icon-margin-right-xsmall no-background-image',
-          cancelButtonClass: 'button text-xsmall text-bold text-center reversed color-primary bordered icon icon-exclamation icon-margin-right-xsmall no-background-image',
-          confirmButtonText: orbit.intl.t('serviceWorker.yes'),
-          cancelButtonText: orbit.intl.t('serviceWorker.no')
-        }).then((result) => {
-          if (result.isConfirmed) {
-            wb.addEventListener('controlling', (event) => {
-              window.location.reload();
-            });
-
-            if (registration && registration.waiting) {
-              messageSW(registration.waiting, {
-                type: 'SKIP_WAITING'
-              });
-            }
-          }
-        });
-      }
-    };
-
-    wb.addEventListener('waiting', showSkipWaitingPrompt);
-    wb.addEventListener('externalwaiting', showSkipWaitingPrompt);
-
-    wb.register().then((r) => registration = r);
-  });
-}
\ No newline at end of file
diff --git a/src/index.pug b/src/index.pug
index 1ac36057..bb4ab8f8 100644
--- a/src/index.pug
+++ b/src/index.pug
@@ -27,9 +27,6 @@ html(lang="en")
 
     include orbit-envoy.pug
 
-    script(type="module" src="/components/orbit-auto-login.js" defer)
-    script(type="module" src="/components/orbit-reactivity.js" defer)
-    script(type="module" src="/components/sw-toolbox.js" defer)
     script(src="index.js" defer)
 
     include orbit-dependencies.pug
diff --git a/src/libs/sw-toolbox.js b/src/libs/sw-toolbox.js
new file mode 100644
index 00000000..df72978a
--- /dev/null
+++ b/src/libs/sw-toolbox.js
@@ -0,0 +1,44 @@
+import(`https://storage.googleapis.com/workbox-cdn/releases/6.1.1/workbox-window.prod.mjs`).then(workboxWindow => {
+  if ('serviceWorker' in navigator) {
+    window.addEventListener('load', function () {
+
+      const wb = new workboxWindow.Workbox('/service-worker.js');
+      let registration;
+
+      const showSkipWaitingPrompt = (event) => {
+        if (orbit.intl.t('serviceWorker.newUpdate') != undefined) {
+          Swal.fire({
+            position: 'bottom-end',
+            backdrop: false,
+            title: "",
+            text: orbit.intl.t('serviceWorker.newUpdate') + ". " + orbit.intl.t('serviceWorker.wantToUpdate'),
+            imageUrl: orbit.client.logo || 'https://cdn.startinblox.com/logos/webp/startinblox.webp',
+            imageAlt: orbit.client.name,
+            showCancelButton: true,
+            confirmButtonClass: 'button text-xsmall text-bold text-center reversed color-secondary bordered icon icon-check icon-margin-right-xsmall no-background-image',
+            cancelButtonClass: 'button text-xsmall text-bold text-center reversed color-primary bordered icon icon-exclamation icon-margin-right-xsmall no-background-image',
+            confirmButtonText: orbit.intl.t('serviceWorker.yes'),
+            cancelButtonText: orbit.intl.t('serviceWorker.no')
+          }).then((result) => {
+            if (result.isConfirmed) {
+              wb.addEventListener('controlling', (event) => {
+                window.location.reload();
+              });
+
+              if (registration && registration.waiting) {
+                workboxWindow.messageSW(registration.waiting, {
+                  type: 'SKIP_WAITING'
+                });
+              }
+            }
+          });
+        }
+      };
+
+      wb.addEventListener('waiting', showSkipWaitingPrompt);
+      wb.addEventListener('externalwaiting', showSkipWaitingPrompt);
+
+      wb.register().then((r) => registration = r);
+    });
+  }
+});
\ No newline at end of file
diff --git a/src/orbit-dependencies.pug b/src/orbit-dependencies.pug
index 2b47871c..6cd3b9fd 100644
--- a/src/orbit-dependencies.pug
+++ b/src/orbit-dependencies.pug
@@ -1,10 +1,7 @@
-script(type="module" src="https://cdn.skypack.dev/@startinblox/core@0.17" defer)
-//- script(type="module" src="/lib/sib-core/dist/index.js" defer)
-
-script(type="module" src="https://cdn.skypack.dev/@startinblox/router@0.11" defer)
-//- script(type="module" src="/lib/sib-router/src/index.js" defer)
-
 -
+  if(!npm) {
+    var npm = [];
+  }
   const componentSet = new Set(components.map(c=>c.type));
   components.map(c => {
     if(c.extensions) {
@@ -12,53 +9,112 @@ script(type="module" src="https://cdn.skypack.dev/@startinblox/router@0.11" defe
     }
   });
 
-if componentSet.has("autoLogin") || componentSet.has("registering")
-  script(type="module" src="https://cdn.skypack.dev/@startinblox/oidc@0.15/dist/index.js" defer)
-  //- script(type="module" src="/lib/sib-auth/index.js" defer)
+  //- Managing default versions
+  if(!npm.find(e => e.package == "@startinblox/core"))
+    npm.push({
+      "package": "@startinblox/core",
+      "version": "0.17"
+    })
+
+  if(!npm.find(e => e.package == "@startinblox/router"))
+    npm.push({
+      "package": "@startinblox/router",
+      "version": "0.11"
+    })
 
-if componentSet.has("chat") || componentSet.has("circles") || componentSet.has("projects")
-  script(type="module" src="https://cdn.skypack.dev/@startinblox/component-chat@6.3" defer)
-  //- script(type="module" src="/lib/solid-xmpp-chat/dist/index.js" defer)
+  if(componentSet.has("autoLogin") || componentSet.has("registering"))
+    if(!npm.find(e => e.package == "@startinblox/oidc"))
+      npm.push({
+        "package": "@startinblox/oidc",
+        "version": "0.15"
+      })
 
-if componentSet.has("communities")
-  script(type="module" src="https://cdn.skypack.dev/@startinblox/core@0.17/dist/components/solid-map.js" defer)
-  //- script(type="module" src="/lib/sib-core/dist/components/solid-map.js" defer)
+  if(componentSet.has("chat") || componentSet.has("circles") || componentSet.has("projects"))
+    if(!npm.find(e => e.package == "@startinblox/chat"))
+      npm.push({
+        "package": "@startinblox/component-chat",
+        "version": "6.3"
+      })
 
-if componentSet.has("dashboard")
-  script(type="module" src="https://cdn.skypack.dev/@startinblox/component-dashboard@5.1" defer)
-  //- script(type="module" src="/lib/solid-dashboard/dist/index.js" defer)
+  if(componentSet.has("communities"))
+    if(!npm.find(e => e.package == "@startinblox/core" && e.version == "0.17/dist/components/solid-map.js"))
+      npm.push({
+        "package": "@startinblox/core",
+        "version": "0.17/dist/components/solid-map.js"
+      })
 
-if componentSet.has("events")
-  script(type="module", src="https://cdn.skypack.dev/@startinblox/component-event@4.1", defer)
-  //- script(type="module", src="/lib/solid-event/solid-event.js", defer)
+  if(componentSet.has("dashboard"))
+    if(!npm.find(e => e.package == "@startinblox/component-dashboard"))
+      npm.push({
+        "package": "@startinblox/component-dashboard",
+        "version": "5.1"
+      })
 
-if componentSet.has("events") || componentSet.has("polls") || componentSet.has("resources")
-  script(type="module" src="https://cdn.skypack.dev/@startinblox/component-conversation@0.11" defer)
+  if(componentSet.has("events"))
+    if(!npm.find(e => e.package == "@startinblox/component-event"))
+      npm.push({
+        "package": "@startinblox/component-event",
+        "version": "4.1"
+      })
 
-if componentSet.has('invoices')
-  script(type="module" src="https://cdn.skypack.dev/@startinblox/component-invoicing@1.4" defer)
-  //- script(type="module" src="/lib/solid-invoicing/solid-invoicing.js" defer)
+  if(componentSet.has("events") || componentSet.has("polls") || componentSet.has("resources"))
+    if(!npm.find(e => e.package == "@startinblox/component-conversation"))
+      npm.push({
+        "package": "@startinblox/component-conversation",
+        "version": "0.11"
+      })
 
-if componentSet.has("job-board")
-  script(type="module" src="https://cdn.skypack.dev/@startinblox/component-job-board@6.2" defer)
-  //- script(type="module" src="/lib/solid-job-board/dist/index.js" defer)
+  if(componentSet.has('invoices'))
+    if(!npm.find(e => e.package == "@startinblox/component-invoicing"))
+      npm.push({
+        "package": "@startinblox/component-invoicing",
+        "version": "1.4"
+      })
 
-if componentSet.has("notification")
-  script(type="module" src="https://cdn.skypack.dev/@startinblox/component-notifications@0.13" defer)
-  //- script(type="module" src="/lib/sib-notifications/index.js" defer)
+  if(componentSet.has("job-board"))
+    if(!npm.find(e => e.package == "@startinblox/component-job-board"))
+      npm.push({
+        "package": "@startinblox/component-job-board",
+        "version": "6.2"
+      })
 
-if componentSet.has("polls")
-  script(type="module" src="https://cdn.skypack.dev/@startinblox/component-poll@3.1" defer)
-  //- script(type="module" src="/lib/solid-poll/index.js" defer)
+  if(componentSet.has("notification"))
+    if(!npm.find(e => e.package == "@startinblox/component-notifications"))
+      npm.push({
+        "package": "@startinblox/component-notifications",
+        "version": "0.13"
+      })
 
-if componentSet.has("directory")
-  script(type="module" src="https://cdn.skypack.dev/@startinblox/component-directory@6.1" defer)
-  //- script(type="module" src="/lib/solid-directory/dist/index.js" defer)
+  if(componentSet.has("polls"))
+    if(!npm.find(e => e.package == "@startinblox/component-poll"))
+      npm.push({
+        "package": "@startinblox/component-poll",
+        "version": "3.1"
+      })
 
-if componentSet.has("resources")
-  script(type="module", src="https://cdn.skypack.dev/@startinblox/component-resource@4.0", defer)
-  //- script(type="module" src="/lib/solid-resource/solid-resource.js" defer)
+  if(componentSet.has("directory"))
+    if(!npm.find(e => e.package == "@startinblox/component-directory"))
+      npm.push({
+        "package": "@startinblox/component-directory",
+        "version": "6.1"
+      })
+
+  if(componentSet.has("resources"))
+    if(!npm.find(e => e.package == "@startinblox/component-resource"))
+      npm.push({
+        "package": "@startinblox/component-resource",
+        "version": "4.0"
+      })
+
+for dependency of npm
+  if dependency.path
+    script(type="module" src=dependency.path defer)&attributes(dependency.attributes || {})
+  else
+    script(type="module" src=`https://cdn.skypack.dev/${dependency.package}@${dependency.version}` defer)&attributes(dependency.attributes || {})
 
 if componentSet.has("themeChecker")
   script(src="https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/pickr.min.js" defer)
   link(rel='stylesheet', href='https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/themes/nano.min.css')
+
+- const orbitVersions = `window.orbit.npm = ${JSON.stringify(npm)};`;
+script!=orbitVersions
diff --git a/src/scripts/orbit-auto-login.js b/src/scripts/orbit-auto-login.js
new file mode 100644
index 00000000..938a6091
--- /dev/null
+++ b/src/scripts/orbit-auto-login.js
@@ -0,0 +1,21 @@
+let core = orbit.npm.find(e => e.package == "@startinblox/core");
+import(`https://cdn.skypack.dev/@startinblox/core@${core.version}`).then(core => {
+  core.Sib.register({
+    name: 'orbit-auto-login',
+    created() {
+      if (window.location.pathname == "/login") {
+        document
+          .querySelectorAll(".loggedIn-loader")
+          .forEach(el => (el.style.display = "flex"));
+        window.dispatchEvent(
+          new CustomEvent('requestNavigation', {
+            detail: {
+              route: window.orbit.getRoute((window.orbit.defaultRoute || "dashboard"), true)
+            }
+          }),
+        );
+        document.querySelector("sib-auth").login();
+      }
+    }
+  });
+})
\ No newline at end of file
diff --git a/src/scripts/orbit-reactivity.js b/src/scripts/orbit-reactivity.js
new file mode 100644
index 00000000..05dd9d15
--- /dev/null
+++ b/src/scripts/orbit-reactivity.js
@@ -0,0 +1,49 @@
+let core = orbit.npm.find(e => e.package == "@startinblox/core");
+import(`https://cdn.skypack.dev/@startinblox/core@${core.version}`).then(core => {
+  core.Sib.register({
+    name: 'orbit-reactivity',
+    use: [core.StoreMixin],
+    attributes: {
+      targetSrc: {
+        type: String,
+        default: '',
+        callback: function () {
+          this.subscribe();
+        }
+      }
+    },
+    async fetchData(value) {
+      this.resourceId = null;
+      if (this.nestedField) {
+        const resource = core.store.get(value) || await core.store.getData(value, this.context);
+        const nestedResource = await resource[this.nestedField]
+        this.resourceId = nestedResource ? nestedResource['@id'] : null;
+      } else {
+        this.resourceId = value;
+      }
+      this.subscribe();
+    },
+    unsubscribe(resourceId, targetSrc) {
+      const resourcesSub = core.store.subscriptionVirtualContainersIndex.get(resourceId);
+      const targetSub = core.store.subscriptionVirtualContainersIndex.get(targetSrc);
+      const newResourceSub = resourcesSub.filter(r => r !== targetSrc);
+      const newTargetSub = targetSub.filter(r => r !== resourceId);
+      core.store.subscriptionVirtualContainersIndex.set(resourceId, newResourceSub);
+      core.store.subscriptionVirtualContainersIndex.set(targetSrc, newTargetSub);
+    },
+    detached() {
+      this.unsubscribe(this.resourceId, this.targetSrc)
+    },
+    subscribe() {
+      if (this.oldResourceId && this.oldTargetSrc && (this.oldResourceId !== this.resourceId || this.oldTargetSrc !== this.targetSrc)) {
+        this.unsubscribe(this.oldResourceId, this.oldTargetSrc);
+      }
+      if (this.resourceId && this.targetSrc) {
+        core.store.subscribeVirtualContainerTo(this.resourceId, this.targetSrc);
+        core.store.subscribeVirtualContainerTo(this.targetSrc, this.resourceId);
+        this.oldResourceId = this.resourceId;
+        this.oldTargetSrc = this.targetSrc;
+      }
+    }
+  });
+});
\ No newline at end of file
-- 
GitLab


From 287666e56050a96cf78bf85da983f85b7db03249 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Pasquier <contact@jbpasquier.eu>
Date: Mon, 5 Jul 2021 18:29:02 +0200
Subject: [PATCH 14/18] fix: getRoute within envoy

---
 src/libs/getRoute.js                    | 23 --------------------
 src/libs/sw-toolbox.js                  |  2 +-
 src/orbit-envoy.pug                     | 28 +++++++++++++++++++++++--
 src/scripts/login-element-visibility.js |  2 +-
 4 files changed, 28 insertions(+), 27 deletions(-)
 delete mode 100644 src/libs/getRoute.js

diff --git a/src/libs/getRoute.js b/src/libs/getRoute.js
deleted file mode 100644
index 10050fc8..00000000
--- a/src/libs/getRoute.js
+++ /dev/null
@@ -1,23 +0,0 @@
-window.orbit.getRoute = (type, returnFirst = false) => {
-  let availables = window.orbit.components.filter(c => c.type == type || c.uniq == type);
-  window.orbit.components.forEach(c => {
-    if (c.extensions) {
-      c.extensions.forEach(e => {
-        if (e.type == type || e.uniq == type) {
-          availables.push(e);
-        }
-      });
-    }
-  });
-  if (availables.length > 1) {
-    if (returnFirst) {
-      return availables[0].route;
-    } else {
-      return availables[availables.length - 1].route;
-    }
-  } else if (availables.length < 1) {
-    console.error(`No component found for route ${type}`);
-  } else {
-    return availables[0].route;
-  }
-}
\ No newline at end of file
diff --git a/src/libs/sw-toolbox.js b/src/libs/sw-toolbox.js
index df72978a..b32edac1 100644
--- a/src/libs/sw-toolbox.js
+++ b/src/libs/sw-toolbox.js
@@ -1,4 +1,4 @@
-import(`https://storage.googleapis.com/workbox-cdn/releases/6.1.1/workbox-window.prod.mjs`).then(workboxWindow => {
+import(`https://storage.googleapis.com/workbox-cdn/releases/6.1.5/workbox-window.prod.mjs`).then(workboxWindow => {
   if ('serviceWorker' in navigator) {
     window.addEventListener('load', function () {
 
diff --git a/src/orbit-envoy.pug b/src/orbit-envoy.pug
index fa3c25de..a647728c 100644
--- a/src/orbit-envoy.pug
+++ b/src/orbit-envoy.pug
@@ -196,5 +196,29 @@ for component of components
     defaultRoute = defaultComponent[0].uniq;
   }
 
-- const orbitComponents = `window.orbit={};window.orbit.components = ${JSON.stringify(components)};window.orbit.federations = ${JSON.stringify(federations)};window.orbit.defaultRoute = "${defaultRoute}";window.orbit.client = ${JSON.stringify(client)};window.hubl = window.orbit;`;
-script!=orbitComponents
\ No newline at end of file
+- const orbitComponents = `window.orbit={};window.orbit.components = ${JSON.stringify(components)};window.orbit.federations = ${JSON.stringify(federations)};window.orbit.defaultRoute = "${defaultRoute}";window.orbit.client = ${JSON.stringify(client)};`;
+script!=orbitComponents
+  |  window.orbit.getRoute = (type, returnFirst = false) => {
+  |    let availables = window.orbit.components.filter(c => c.type == type || c.uniq == type);
+  |    window.orbit.components.forEach(c => {
+  |      if (c.extensions) {
+  |        c.extensions.forEach(e => {
+  |          if (e.type == type || e.uniq == type) {
+  |            availables.push(e);
+  |          }
+  |        });
+  |      }
+  |    });
+  |    if (availables.length > 1) {
+  |      if (returnFirst) {
+  |        return availables[0].route;
+  |      } else {
+  |        return availables[availables.length - 1].route;
+  |      }
+  |    } else if (availables.length < 1) {
+  |      console.error(`No component found for route ${type}`);
+  |    } else {
+  |      return availables[0].route;
+  |    }
+  |  }
+  |  window.hubl = window.orbit;
\ No newline at end of file
diff --git a/src/scripts/login-element-visibility.js b/src/scripts/login-element-visibility.js
index 2d77db64..d304f3e3 100644
--- a/src/scripts/login-element-visibility.js
+++ b/src/scripts/login-element-visibility.js
@@ -36,7 +36,7 @@ document.addEventListener("DOMContentLoaded", function () {
   }
 });
 window.addEventListener("navigate", e => {
-  if (e.detail.route == "login" && !window.requestLogin) {
+  if (e.detail.route == "login" && !window.requestLogin && window.orbit.getRoute) {
     window.dispatchEvent(
       new CustomEvent('requestNavigation', {
         detail: {
-- 
GitLab


From 8b25230ecbb262cf73425745e639c7b870687103 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Pasquier <contact@jbpasquier.eu>
Date: Mon, 5 Jul 2021 18:56:14 +0200
Subject: [PATCH 15/18] cicd: cypress 7.6

---
 .gitlab-ci.yml    |   2 +-
 package-lock.json | 213 +++++++++++++++-------------------------------
 package.json      |   4 +-
 3 files changed, 72 insertions(+), 147 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index bc4ec202..87f6ee1b 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -41,7 +41,7 @@ build:
 
 test:e2e:
   stage: test
-  image: cypress/included:7.5.0
+  image: cypress/included:7.6.0
   services:
     - name: ${CI_REGISTRY_IMAGE}/djangoldp:latest
   before_script:
diff --git a/package-lock.json b/package-lock.json
index ef41bdbd..a5296ba8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1404,45 +1404,6 @@
         "to-fast-properties": "^2.0.0"
       }
     },
-    "@cypress/listr-verbose-renderer": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz",
-      "integrity": "sha1-p3SS9LEdzHxEajSz4ochr9M8ZCo=",
-      "dev": true,
-      "requires": {
-        "chalk": "^1.1.3",
-        "cli-cursor": "^1.0.2",
-        "date-fns": "^1.27.2",
-        "figures": "^1.7.0"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          }
-        },
-        "supports-color": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-          "dev": true
-        }
-      }
-    },
     "@cypress/request": {
       "version": "2.88.5",
       "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.5.tgz",
@@ -1621,9 +1582,9 @@
       "integrity": "sha512-cRo8xUhIZqk4Op+2RjnR7NIc2NZaGhIQRt90LkhnroNJk4D7SwEgA5bZfk/EgCOO/UVRRb0Rm7Nx2wW03AMOiw=="
     },
     "@types/node": {
-      "version": "14.17.3",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.3.tgz",
-      "integrity": "sha512-e6ZowgGJmTuXa3GyaPbTGxX17tnThl2aSSizrFthQ7m9uLGZBXiGhgE55cjRZTF5kjZvYn9EOPOMljdjwbflxw==",
+      "version": "14.17.4",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.4.tgz",
+      "integrity": "sha512-8kQ3+wKGRNN0ghtEn7EGps/B8CzuBz1nXZEIGGLP2GnwbqYn4dbTs7k+VKLTq1HvZLRCIDtN3Snx1Ege8B7L5A==",
       "dev": true
     },
     "@types/q": {
@@ -1644,9 +1605,9 @@
       "dev": true
     },
     "@types/yauzl": {
-      "version": "2.9.1",
-      "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz",
-      "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==",
+      "version": "2.9.2",
+      "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz",
+      "integrity": "sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==",
       "dev": true,
       "optional": true,
       "requires": {
@@ -1710,6 +1671,12 @@
       "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz",
       "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM="
     },
+    "ansi-colors": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+      "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+      "dev": true
+    },
     "ansi-escapes": {
       "version": "4.3.2",
       "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
@@ -2535,12 +2502,12 @@
       "dev": true
     },
     "cli-cursor": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz",
-      "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=",
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+      "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
       "dev": true,
       "requires": {
-        "restore-cursor": "^1.0.1"
+        "restore-cursor": "^3.1.0"
       }
     },
     "cli-spinners": {
@@ -3088,12 +3055,11 @@
       }
     },
     "cypress": {
-      "version": "7.5.0",
-      "resolved": "https://registry.npmjs.org/cypress/-/cypress-7.5.0.tgz",
-      "integrity": "sha512-tw3v6nrTJoEzT37+Nf6RK+DvdTfhMb8EJYskZx7oskZ+J9qQ1QHWA4dH8Eoe/Mr/wE47o+7PK6O9tgqhRy6IHg==",
+      "version": "7.6.0",
+      "resolved": "https://registry.npmjs.org/cypress/-/cypress-7.6.0.tgz",
+      "integrity": "sha512-tTwQExY28CKt6cY85/2V1uLExcMfpBEBWXt/EcE2ht/Onl9k4lxUS7ul1UnUO5MrYwMIHMdGVh13DxdzXj4Z5w==",
       "dev": true,
       "requires": {
-        "@cypress/listr-verbose-renderer": "^0.4.1",
         "@cypress/request": "^2.88.5",
         "@cypress/xvfb": "^1.2.4",
         "@types/node": "^14.14.31",
@@ -3105,15 +3071,18 @@
         "cachedir": "^2.3.0",
         "chalk": "^4.1.0",
         "check-more-types": "^2.24.0",
+        "cli-cursor": "^3.1.0",
         "cli-table3": "~0.6.0",
         "commander": "^5.1.0",
         "common-tags": "^1.8.0",
         "dayjs": "^1.10.4",
-        "debug": "4.3.2",
+        "debug": "^4.3.2",
+        "enquirer": "^2.3.6",
         "eventemitter2": "^6.4.3",
         "execa": "4.1.0",
         "executable": "^4.1.1",
         "extract-zip": "2.0.1",
+        "figures": "^3.2.0",
         "fs-extra": "^9.1.0",
         "getos": "^3.2.1",
         "is-ci": "^3.0.0",
@@ -3238,12 +3207,6 @@
         "whatwg-url": "^7.0.0"
       }
     },
-    "date-fns": {
-      "version": "1.30.1",
-      "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz",
-      "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==",
-      "dev": true
-    },
     "dayjs": {
       "version": "1.10.5",
       "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.5.tgz",
@@ -3501,6 +3464,15 @@
         "once": "^1.4.0"
       }
     },
+    "enquirer": {
+      "version": "2.3.6",
+      "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
+      "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==",
+      "dev": true,
+      "requires": {
+        "ansi-colors": "^4.1.1"
+      }
+    },
     "entities": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
@@ -3655,23 +3627,6 @@
         "onetime": "^5.1.0",
         "signal-exit": "^3.0.2",
         "strip-final-newline": "^2.0.0"
-      },
-      "dependencies": {
-        "mimic-fn": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
-          "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
-          "dev": true
-        },
-        "onetime": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
-          "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
-          "dev": true,
-          "requires": {
-            "mimic-fn": "^2.1.0"
-          }
-        }
       }
     },
     "executable": {
@@ -3683,12 +3638,6 @@
         "pify": "^2.2.0"
       }
     },
-    "exit-hook": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz",
-      "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=",
-      "dev": true
-    },
     "expand-brackets": {
       "version": "2.1.4",
       "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
@@ -3875,13 +3824,12 @@
       }
     },
     "figures": {
-      "version": "1.7.0",
-      "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
-      "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=",
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
+      "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
       "dev": true,
       "requires": {
-        "escape-string-regexp": "^1.0.5",
-        "object-assign": "^4.1.0"
+        "escape-string-regexp": "^1.0.5"
       }
     },
     "file-uri-to-path": {
@@ -5617,15 +5565,6 @@
             "color-convert": "^2.0.1"
           }
         },
-        "cli-cursor": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
-          "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
-          "dev": true,
-          "requires": {
-            "restore-cursor": "^3.1.0"
-          }
-        },
         "color-convert": {
           "version": "2.0.1",
           "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -5641,31 +5580,6 @@
           "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
           "dev": true
         },
-        "mimic-fn": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
-          "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
-          "dev": true
-        },
-        "onetime": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
-          "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
-          "dev": true,
-          "requires": {
-            "mimic-fn": "^2.1.0"
-          }
-        },
-        "restore-cursor": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
-          "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
-          "dev": true,
-          "requires": {
-            "onetime": "^5.1.0",
-            "signal-exit": "^3.0.2"
-          }
-        },
         "slice-ansi": {
           "version": "4.0.0",
           "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
@@ -6184,10 +6098,21 @@
       }
     },
     "onetime": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
-      "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=",
-      "dev": true
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+      "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+      "dev": true,
+      "requires": {
+        "mimic-fn": "^2.1.0"
+      },
+      "dependencies": {
+        "mimic-fn": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+          "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+          "dev": true
+        }
+      }
     },
     "opn": {
       "version": "5.5.0",
@@ -7695,13 +7620,13 @@
       "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo="
     },
     "restore-cursor": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz",
-      "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=",
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+      "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
       "dev": true,
       "requires": {
-        "exit-hook": "^1.0.0",
-        "onetime": "^1.0.0"
+        "onetime": "^5.1.0",
+        "signal-exit": "^3.0.2"
       }
     },
     "ret": {
@@ -7764,9 +7689,9 @@
       "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
     },
     "sass": {
-      "version": "1.35.0",
-      "resolved": "https://registry.npmjs.org/sass/-/sass-1.35.0.tgz",
-      "integrity": "sha512-9joxNu7CoIEdZTDZFnH67NoWE/1VTofGPYiSIntWSE391SfmPu6Kd2Y0ZclDu9WQhVvvPwvo8LHeb/8wHIj95Q==",
+      "version": "1.35.1",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.35.1.tgz",
+      "integrity": "sha512-oCisuQJstxMcacOPmxLNiLlj4cUyN2+8xJnG7VanRoh2GOLr9RqkvI4AxA4a6LHVg/rsu+PmxXeGhrdSF9jCiQ==",
       "requires": {
         "chokidar": ">=3.0.0 <4.0.0"
       },
@@ -7794,18 +7719,18 @@
           }
         },
         "chokidar": {
-          "version": "3.5.1",
-          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz",
-          "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==",
+          "version": "3.5.2",
+          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
+          "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
           "requires": {
-            "anymatch": "~3.1.1",
+            "anymatch": "~3.1.2",
             "braces": "~3.0.2",
-            "fsevents": "~2.3.1",
-            "glob-parent": "~5.1.0",
+            "fsevents": "~2.3.2",
+            "glob-parent": "~5.1.2",
             "is-binary-path": "~2.1.0",
             "is-glob": "~4.0.1",
             "normalize-path": "~3.0.0",
-            "readdirp": "~3.5.0"
+            "readdirp": "~3.6.0"
           }
         },
         "fill-range": {
@@ -7844,9 +7769,9 @@
           "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
         },
         "readdirp": {
-          "version": "3.5.0",
-          "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz",
-          "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==",
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+          "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
           "requires": {
             "picomatch": "^2.2.1"
           }
diff --git a/package.json b/package.json
index bd886c27..34a9f35e 100644
--- a/package.json
+++ b/package.json
@@ -71,10 +71,10 @@
     "parcel-plugin-sw-cache": "^0.3.1",
     "pug": "^3.0.2",
     "rimraf": "^3.0.2",
-    "sass": "^1.35.0"
+    "sass": "^1.35.1"
   },
   "devDependencies": {
-    "cypress": "^7.5.0",
+    "cypress": "^7.6.0",
     "cypress-localstorage-commands": "^1.4.5"
   }
 }
-- 
GitLab


From 8c860e07aab14a4a13838d9addfe226972fd0848 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Pasquier <contact@jbpasquier.eu>
Date: Mon, 5 Jul 2021 18:58:22 +0200
Subject: [PATCH 16/18] fix: versions

---
 package-lock.json | 45 ++++++++++++++++++++++++++-------------------
 1 file changed, 26 insertions(+), 19 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index a5296ba8..062d1e1c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2236,15 +2236,22 @@
       }
     },
     "browserslist": {
-      "version": "4.16.3",
-      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz",
-      "integrity": "sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==",
+      "version": "4.16.6",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz",
+      "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==",
       "requires": {
-        "caniuse-lite": "^1.0.30001181",
-        "colorette": "^1.2.1",
-        "electron-to-chromium": "^1.3.649",
+        "caniuse-lite": "^1.0.30001219",
+        "colorette": "^1.2.2",
+        "electron-to-chromium": "^1.3.723",
         "escalade": "^3.1.1",
-        "node-releases": "^1.1.70"
+        "node-releases": "^1.1.71"
+      },
+      "dependencies": {
+        "caniuse-lite": {
+          "version": "1.0.30001242",
+          "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001242.tgz",
+          "integrity": "sha512-KvNuZ/duufelMB3w2xtf9gEWCSxJwUgoxOx5b6ScLXC4kPc9xsczUVCPrQU26j5kOsHM4pSUL54tAZt5THQKug=="
+        }
       }
     },
     "buffer": {
@@ -3420,9 +3427,9 @@
       "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
     },
     "electron-to-chromium": {
-      "version": "1.3.710",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.710.tgz",
-      "integrity": "sha512-b3r0E2o4yc7mNmBeJviejF1rEx49PUBi+2NPa7jHEX3arkAXnVgLhR0YmV8oi6/Qf3HH2a8xzQmCjHNH0IpXWQ=="
+      "version": "1.3.766",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.766.tgz",
+      "integrity": "sha512-u2quJ862q9reRKh/je3GXis3w38+RoXH1J9N3XjtsS6NzmUAosNsyZgUVFZPN/ZlJ3v6T0rTyZR3q/J5c6Sy5w=="
     },
     "elliptic": {
       "version": "6.5.4",
@@ -5308,9 +5315,9 @@
           "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
         },
         "ws": {
-          "version": "6.2.1",
-          "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
-          "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
+          "version": "6.2.2",
+          "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
+          "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==",
           "requires": {
             "async-limiter": "~1.0.0"
           }
@@ -5426,9 +5433,9 @@
       }
     },
     "lodash": {
-      "version": "4.17.20",
-      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
-      "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
     },
     "lodash._reinterpolate": {
       "version": "3.0.0",
@@ -5914,9 +5921,9 @@
       }
     },
     "node-releases": {
-      "version": "1.1.71",
-      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz",
-      "integrity": "sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg=="
+      "version": "1.1.73",
+      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz",
+      "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg=="
     },
     "normalize-path": {
       "version": "3.0.0",
-- 
GitLab


From 6d52e869d165a4c3b64cdf6f6d44c08f4d075309 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Pasquier <contact@jbpasquier.eu>
Date: Mon, 5 Jul 2021 19:15:43 +0200
Subject: [PATCH 17/18] feature: don't create federation when there is only one
 target

---
 src/orbit-envoy.pug | 46 +++++++++++++++++++++++++++------------------
 1 file changed, 28 insertions(+), 18 deletions(-)

diff --git a/src/orbit-envoy.pug b/src/orbit-envoy.pug
index a647728c..3c405c5e 100644
--- a/src/orbit-envoy.pug
+++ b/src/orbit-envoy.pug
@@ -113,15 +113,20 @@ for component of components
       for(const [attribute, path] of Object.entries(component.parameters)) {
         if(typeof path === 'string') {
           if(path.startsWith('federation://')) {
-            federations[`store://local.${component.uniq}/${attribute}/`] = {
-              "@cache": "false",
-              "@context": "https://cdn.happy-dev.fr/owl/hdcontext.jsonld",
-              "@type": "ldp:Container",
-              "@id": `store://local.${component.uniq}/${attribute}/`,
-              "ldp:contains": generateUrl(federation, path),
-              "permissions": [{"mode": {"@type": "view"}}]
-            };
-            component.parameters[attribute] = `store://local.${component.uniq}/${attribute}/`;
+            let contains = generateUrl(federation, path);
+            if(contains.length > 1) {
+              federations[`store://local.${component.uniq}/${attribute}/`] = {
+                "@cache": "false",
+                "@context": "https://cdn.happy-dev.fr/owl/hdcontext.jsonld",
+                "@type": "ldp:Container",
+                "@id": `store://local.${component.uniq}/${attribute}/`,
+                "ldp:contains": contains,
+                "permissions": [{"mode": {"@type": "view"}}]
+              };
+              component.parameters[attribute] = `store://local.${component.uniq}/${attribute}/`;
+            } else {
+              component.parameters[attribute] = federation.values().next().value + path.replace(/federation:\//, '');
+            }
           }
           if(path.startsWith('server://')) {
             component.parameters[attribute] = client.server + path.replace(/server:\//, '');
@@ -163,15 +168,20 @@ for component of components
           for(const [attribute, path] of Object.entries(extension.parameters)) {
             if(typeof path === 'string') {
               if(path.startsWith('federation://')) {
-                federations[`store://local.${extension.uniq}/${attribute}/`] = {
-                  "@cache": "false",
-                  "@context": "https://cdn.happy-dev.fr/owl/hdcontext.jsonld",
-                  "@type": "ldp:Container",
-                  "@id": `store://local.${extension.uniq}/${attribute}/`,
-                  "ldp:contains": generateUrl(federation, path),
-                  "permissions": [{"mode": {"@type": "view"}}]
-                };
-                extension.parameters[attribute] = `store://local.${extension.uniq}/${attribute}/`;
+                let contains = generateUrl(federation, path);
+                if(contains.length > 1) {
+                  federations[`store://local.${extension.uniq}/${attribute}/`] = {
+                    "@cache": "false",
+                    "@context": "https://cdn.happy-dev.fr/owl/hdcontext.jsonld",
+                    "@type": "ldp:Container",
+                    "@id": `store://local.${extension.uniq}/${attribute}/`,
+                    "ldp:contains": contains,
+                    "permissions": [{"mode": {"@type": "view"}}]
+                  };
+                  extension.parameters[attribute] = `store://local.${extension.uniq}/${attribute}/`;
+                } else {
+                  component.parameters[attribute] = federation.values().next().value + path.replace(/federation:\//, '');
+                }
               }
               if(path.startsWith('server://')) {
                 extension.parameters[attribute] = client.server + path.replace(/server:\//, '');
-- 
GitLab


From 8b055159c74c9ff60f0e05c64cc63a3788b4e7ac Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Pasquier <contact@jbpasquier.eu>
Date: Mon, 5 Jul 2021 19:19:59 +0200
Subject: [PATCH 18/18] fix: deprecated variable

---
 src/orbit-envoy.pug | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/src/orbit-envoy.pug b/src/orbit-envoy.pug
index 3c405c5e..5e460625 100644
--- a/src/orbit-envoy.pug
+++ b/src/orbit-envoy.pug
@@ -10,8 +10,6 @@
   ```
   will return the route of the first chat component, if exists, or triggers an error.
 
-
-  Okay. This file really needs a cleanup now... Emergency, have to release for Friday...
 -
   let routes = new Set();
   const getRoute = (type, returnFirst = false) => {
@@ -62,7 +60,6 @@
     return result;
   }
   var federations = {};
-  let federationRegistering = "";
 
 for component of components
   -
-- 
GitLab