infra: add perfetto.dev site

Merge the sources of the new website @ perfetto.dev

Change-Id: Ie3bc59fa3f42a41360bf07118d2ad8e12f409660
diff --git a/infra/perfetto-site.appspot.com/.gitignore b/infra/perfetto-site.appspot.com/.gitignore
deleted file mode 100644
index 4047166..0000000
--- a/infra/perfetto-site.appspot.com/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-node_modules/
-static/
diff --git a/infra/perfetto-site.appspot.com/Makefile b/infra/perfetto-site.appspot.com/Makefile
deleted file mode 100644
index 0811e7f..0000000
--- a/infra/perfetto-site.appspot.com/Makefile
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-test: static
-	dev_appserver.py .
-
-static: node_modules/.stamp
-	rm -rf static
-	mkdir -p static
-	cp node_modules/docsify/lib/docsify.min.js static/
-	cp node_modules/docsify-themeable/dist/js/docsify-themeable.min.js static/
-	cp node_modules/docsify-themeable/dist/css/theme-simple.css static/
-	cp node_modules/docsify-copy-code/dist/docsify-copy-code.min.js static/
-	cp node_modules/prismjs/components/prism-bash.min.js static/
-	cp node_modules/prismjs/components/prism-protobuf.min.js static/
-	cp node_modules/prismjs/components/prism-sql.min.js static/
-
-deploy: static
-	gcloud app deploy app.yaml --project perfetto-site
-
-node_modules/.stamp: package.json
-	npm install --quiet
-	touch "$@"
-
-.PHONY: deploy test static
diff --git a/infra/perfetto-site.appspot.com/app.yaml b/infra/perfetto-site.appspot.com/app.yaml
deleted file mode 100644
index 531f2c1..0000000
--- a/infra/perfetto-site.appspot.com/app.yaml
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-runtime: python27
-api_version: 1
-threadsafe: yes
-skip_files:
- - ^node_modules.*
-handlers:
-- url: /static/
-  static_dir: static/
-  secure: always
-  redirect_http_response_code: 301
-- url: .*
-  script: main.app
-  secure: always
-  redirect_http_response_code: 301
diff --git a/infra/perfetto-site.appspot.com/index.yaml b/infra/perfetto-site.appspot.com/index.yaml
deleted file mode 100644
index eb9fd6f..0000000
--- a/infra/perfetto-site.appspot.com/index.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (C) 2019 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-indexes:
-# AUTOGENERATED
diff --git a/infra/perfetto-site.appspot.com/main.py b/infra/perfetto-site.appspot.com/main.py
deleted file mode 100644
index 4a3618a..0000000
--- a/infra/perfetto-site.appspot.com/main.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from google.appengine.api import memcache
-from google.appengine.api import urlfetch
-
-import re
-import webapp2
-
-MEMCACHE_TTL_SEC = 60 * 60 * 24
-BASE = 'https://google.github.io/perfetto/%s'
-HEADERS = {
-    'last-modified', 'content-type', 'content-length', 'content-encoding',
-    'etag'
-}
-
-
-class MainHandler(webapp2.RequestHandler):
-
-  def get(self):
-    handler = GithubMirrorHandler()
-    handler.initialize(self.request, self.response)
-    return handler.get("index.html")
-
-
-class GithubMirrorHandler(webapp2.RequestHandler):
-
-  def get(self, resource):
-    if not re.match('^[a-zA-Z0-9-_./]*$', resource):
-      self.response.set_status(403)
-      return
-
-    url = BASE % resource
-    cache = memcache.get(url)
-    if not cache or self.request.get('reload'):
-      result = urlfetch.fetch(url)
-      if result.status_code != 200:
-        memcache.delete(url)
-        self.response.set_status(result.status_code)
-        self.response.write(result.content)
-        return
-      cache = {'content-type': 'text/html'}
-      for k, v in result.headers.iteritems():
-        k = k.lower()
-        if k in HEADERS:
-          cache[k] = v
-      cache['content'] = result.content
-      memcache.set(url, cache, time=MEMCACHE_TTL_SEC)
-
-    for k, v in cache.iteritems():
-      if k != 'content':
-        self.response.headers[k] = v
-    self.response.headers['cache-control'] = 'public,max-age=600'
-    self.response.write(cache['content'])
-
-
-app = webapp2.WSGIApplication([
-    ('/', MainHandler),
-    ('/(.+)', GithubMirrorHandler),
-],
-                              debug=True)
diff --git a/infra/perfetto-site.appspot.com/package-lock.json b/infra/perfetto-site.appspot.com/package-lock.json
deleted file mode 100644
index b763809..0000000
--- a/infra/perfetto-site.appspot.com/package-lock.json
+++ /dev/null
@@ -1,424 +0,0 @@
-{
-  "name": "perfetto-docs",
-  "version": "1.0.0",
-  "lockfileVersion": 1,
-  "requires": true,
-  "dependencies": {
-    "ansi-escapes": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz",
-      "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4="
-    },
-    "ansi-regex": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
-    },
-    "ansi-styles": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-      "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
-    },
-    "babel-polyfill": {
-      "version": "6.23.0",
-      "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.23.0.tgz",
-      "integrity": "sha1-g2TKYt+Or7gwSZ9pkXdGbDsDSZ0=",
-      "requires": {
-        "babel-runtime": "^6.22.0",
-        "core-js": "^2.4.0",
-        "regenerator-runtime": "^0.10.0"
-      }
-    },
-    "babel-runtime": {
-      "version": "6.26.0",
-      "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
-      "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
-      "requires": {
-        "core-js": "^2.4.0",
-        "regenerator-runtime": "^0.11.0"
-      },
-      "dependencies": {
-        "regenerator-runtime": {
-          "version": "0.11.1",
-          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
-          "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
-        }
-      }
-    },
-    "chalk": {
-      "version": "1.1.3",
-      "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-      "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-      "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"
-      }
-    },
-    "chardet": {
-      "version": "0.4.2",
-      "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz",
-      "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I="
-    },
-    "cli-cursor": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
-      "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
-      "requires": {
-        "restore-cursor": "^2.0.0"
-      }
-    },
-    "cli-width": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
-      "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk="
-    },
-    "clipboard": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.1.tgz",
-      "integrity": "sha512-7yhQBmtN+uYZmfRjjVjKa0dZdWuabzpSKGtyQZN+9C8xlC788SSJjOHWh7tzurfwTqTD5UDYAhIv5fRJg3sHjQ==",
-      "optional": true,
-      "requires": {
-        "good-listener": "^1.2.2",
-        "select": "^1.1.2",
-        "tiny-emitter": "^2.0.0"
-      }
-    },
-    "core-js": {
-      "version": "2.5.7",
-      "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz",
-      "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw=="
-    },
-    "delegate": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
-      "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==",
-      "optional": true
-    },
-    "docsify": {
-      "version": "4.7.1",
-      "resolved": "https://registry.npmjs.org/docsify/-/docsify-4.7.1.tgz",
-      "integrity": "sha512-RdM4oN9yYNboTlm9JtbTMIqjYjPF7WBF7W37NXCG1fZIBC83Ya8qdH9c1rpPkLYw8HekJzSO2P13f6z/GVKITA==",
-      "requires": {
-        "marked": "^0.3.12",
-        "medium-zoom": "^0.4.0",
-        "opencollective": "^1.0.3",
-        "prismjs": "^1.9.0",
-        "tinydate": "^1.0.0",
-        "tweezer.js": "^1.4.0"
-      }
-    },
-    "docsify-copy-code": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/docsify-copy-code/-/docsify-copy-code-2.0.2.tgz",
-      "integrity": "sha512-jKJUtiWa57NqisVf4ZvbaqzT4Kj+j4uxarogyB8lWrrSbwwsC0t4JR6f7DTLgRbo65US+xZeo8e1s5qkUjIlwA=="
-    },
-    "docsify-themeable": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/docsify-themeable/-/docsify-themeable-0.4.0.tgz",
-      "integrity": "sha512-onJzSH/cZh/Stngj/WR3OAeGI/jLKfB3RhsksnsTFG003lcM1BcpdiLqxAeP4PUxbU4bzj0vC4a8phQDgfscfQ=="
-    },
-    "encoding": {
-      "version": "0.1.12",
-      "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
-      "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
-      "requires": {
-        "iconv-lite": "~0.4.13"
-      }
-    },
-    "escape-string-regexp": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
-    },
-    "external-editor": {
-      "version": "2.2.0",
-      "resolved": "http://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz",
-      "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==",
-      "requires": {
-        "chardet": "^0.4.0",
-        "iconv-lite": "^0.4.17",
-        "tmp": "^0.0.33"
-      }
-    },
-    "figures": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
-      "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
-      "requires": {
-        "escape-string-regexp": "^1.0.5"
-      }
-    },
-    "good-listener": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
-      "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
-      "optional": true,
-      "requires": {
-        "delegate": "^3.1.2"
-      }
-    },
-    "has-ansi": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
-      "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
-      "requires": {
-        "ansi-regex": "^2.0.0"
-      }
-    },
-    "iconv-lite": {
-      "version": "0.4.24",
-      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
-      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
-      "requires": {
-        "safer-buffer": ">= 2.1.2 < 3"
-      }
-    },
-    "inquirer": {
-      "version": "3.0.6",
-      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.0.6.tgz",
-      "integrity": "sha1-4EqqnQW3o8ubD0B9BDdfBEcZA0c=",
-      "requires": {
-        "ansi-escapes": "^1.1.0",
-        "chalk": "^1.0.0",
-        "cli-cursor": "^2.1.0",
-        "cli-width": "^2.0.0",
-        "external-editor": "^2.0.1",
-        "figures": "^2.0.0",
-        "lodash": "^4.3.0",
-        "mute-stream": "0.0.7",
-        "run-async": "^2.2.0",
-        "rx": "^4.1.0",
-        "string-width": "^2.0.0",
-        "strip-ansi": "^3.0.0",
-        "through": "^2.3.6"
-      }
-    },
-    "is-fullwidth-code-point": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-      "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
-    },
-    "is-promise": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
-      "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o="
-    },
-    "is-stream": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
-      "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
-    },
-    "lodash": {
-      "version": "4.17.15",
-      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
-      "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
-    },
-    "marked": {
-      "version": "0.3.19",
-      "resolved": "http://registry.npmjs.org/marked/-/marked-0.3.19.tgz",
-      "integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg=="
-    },
-    "medium-zoom": {
-      "version": "0.4.0",
-      "resolved": "http://registry.npmjs.org/medium-zoom/-/medium-zoom-0.4.0.tgz",
-      "integrity": "sha512-0z7yMfd6I1BTCAa8QaR4cp5AqDkQD571GzhHIbbfefKEssGLSvs+4Xai/itOAncm4FBlF5gUoMQ22yW9/f8Sig=="
-    },
-    "mimic-fn": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
-      "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="
-    },
-    "minimist": {
-      "version": "1.2.0",
-      "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
-      "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
-    },
-    "mute-stream": {
-      "version": "0.0.7",
-      "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
-      "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s="
-    },
-    "node-fetch": {
-      "version": "1.6.3",
-      "resolved": "http://registry.npmjs.org/node-fetch/-/node-fetch-1.6.3.tgz",
-      "integrity": "sha1-3CNO3WSJmC1Y6PDbT2lQKavNjAQ=",
-      "requires": {
-        "encoding": "^0.1.11",
-        "is-stream": "^1.0.1"
-      }
-    },
-    "object-assign": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
-      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
-    },
-    "onetime": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
-      "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
-      "requires": {
-        "mimic-fn": "^1.0.0"
-      }
-    },
-    "opencollective": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/opencollective/-/opencollective-1.0.3.tgz",
-      "integrity": "sha1-ruY3K8KBRFg2kMPKja7PwSDdDvE=",
-      "requires": {
-        "babel-polyfill": "6.23.0",
-        "chalk": "1.1.3",
-        "inquirer": "3.0.6",
-        "minimist": "1.2.0",
-        "node-fetch": "1.6.3",
-        "opn": "4.0.2"
-      }
-    },
-    "opn": {
-      "version": "4.0.2",
-      "resolved": "http://registry.npmjs.org/opn/-/opn-4.0.2.tgz",
-      "integrity": "sha1-erwi5kTf9jsKltWrfyeQwPAavJU=",
-      "requires": {
-        "object-assign": "^4.0.1",
-        "pinkie-promise": "^2.0.0"
-      }
-    },
-    "os-tmpdir": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
-      "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
-    },
-    "pinkie": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
-      "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA="
-    },
-    "pinkie-promise": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
-      "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
-      "requires": {
-        "pinkie": "^2.0.0"
-      }
-    },
-    "prismjs": {
-      "version": "1.15.0",
-      "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.15.0.tgz",
-      "integrity": "sha512-Lf2JrFYx8FanHrjoV5oL8YHCclLQgbJcVZR+gikGGMqz6ub5QVWDTM6YIwm3BuPxM/LOV+rKns3LssXNLIf+DA==",
-      "requires": {
-        "clipboard": "^2.0.0"
-      }
-    },
-    "regenerator-runtime": {
-      "version": "0.10.5",
-      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz",
-      "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg="
-    },
-    "restore-cursor": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
-      "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
-      "requires": {
-        "onetime": "^2.0.0",
-        "signal-exit": "^3.0.2"
-      }
-    },
-    "run-async": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
-      "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
-      "requires": {
-        "is-promise": "^2.1.0"
-      }
-    },
-    "rx": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz",
-      "integrity": "sha1-pfE/957zt0D+MKqAP7CfmIBdR4I="
-    },
-    "safer-buffer": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
-      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
-    },
-    "select": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
-      "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=",
-      "optional": true
-    },
-    "signal-exit": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
-      "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
-    },
-    "string-width": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
-      "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
-      "requires": {
-        "is-fullwidth-code-point": "^2.0.0",
-        "strip-ansi": "^4.0.0"
-      },
-      "dependencies": {
-        "ansi-regex": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
-          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
-        },
-        "strip-ansi": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
-          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
-          "requires": {
-            "ansi-regex": "^3.0.0"
-          }
-        }
-      }
-    },
-    "strip-ansi": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
-      "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
-      "requires": {
-        "ansi-regex": "^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="
-    },
-    "through": {
-      "version": "2.3.8",
-      "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",
-      "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
-    },
-    "tiny-emitter": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.0.2.tgz",
-      "integrity": "sha512-2NM0auVBGft5tee/OxP4PI3d8WItkDM+fPnaRAVo6xTDI2knbz9eC5ArWGqtGlYqiH3RU5yMpdyTTO7MguC4ow==",
-      "optional": true
-    },
-    "tinydate": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/tinydate/-/tinydate-1.0.0.tgz",
-      "integrity": "sha1-IPMXVqE5We+MV+wTO6KbWt4ELKw="
-    },
-    "tmp": {
-      "version": "0.0.33",
-      "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
-      "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
-      "requires": {
-        "os-tmpdir": "~1.0.2"
-      }
-    },
-    "tweezer.js": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/tweezer.js/-/tweezer.js-1.4.0.tgz",
-      "integrity": "sha1-IG/1aK00zw5WoEMH2Z/8Uhk9UEU="
-    }
-  }
-}
diff --git a/infra/perfetto-site.appspot.com/package.json b/infra/perfetto-site.appspot.com/package.json
deleted file mode 100644
index d9018e4..0000000
--- a/infra/perfetto-site.appspot.com/package.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "name": "perfetto-docs",
-  "version": "1.0.0",
-  "main": "index.html",
-  "dependencies": {
-    "docsify": "^4.7.1",
-    "docsify-copy-code": "^2.0.2",
-    "docsify-themeable": "^0.4.0",
-    "prismjs": "^1.15.0"
-  }
-}
diff --git a/infra/perfetto.dev/.gcloudignore b/infra/perfetto.dev/.gcloudignore
new file mode 100644
index 0000000..2382779
--- /dev/null
+++ b/infra/perfetto.dev/.gcloudignore
@@ -0,0 +1,12 @@
+.firebase/
+.firebaserc
+.gcloudignore
+.git
+.gitignore
+BUILD.gn
+deploy
+firebase-debug.log
+node_modules/
+package-lock.json
+package.json
+run-dev-server
\ No newline at end of file
diff --git a/infra/perfetto.dev/.gitignore b/infra/perfetto.dev/.gitignore
new file mode 100644
index 0000000..c8d9b83
--- /dev/null
+++ b/infra/perfetto.dev/.gitignore
@@ -0,0 +1,4 @@
+node_modules/
+.firebase/
+firebase-debug.log
+index.yaml
diff --git a/infra/perfetto.dev/BUILD.gn b/infra/perfetto.dev/BUILD.gn
new file mode 100644
index 0000000..ddab3db
--- /dev/null
+++ b/infra/perfetto.dev/BUILD.gn
@@ -0,0 +1,350 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("../../gn/perfetto.gni")
+
+# Prevent that this file is accidentally included in embedder builds.
+assert(enable_perfetto_ui)
+
+dist_dir = "$root_build_dir/site"
+gen_dir = "$target_out_dir/gen"
+nodejs_root = "../buildtools/nodejs"
+nodejs_bin = rebase_path("$nodejs_root/bin", root_build_dir)
+
+# +----------------------------------------------------------------------------+
+# | The outer "ui" target to just ninja -C out/xxx ui                          |
+# +----------------------------------------------------------------------------+
+
+group("site") {
+  deps = [
+    ":assets_dist",
+    ":index_dist",
+    ":markdown",
+    ":scss",
+  ]
+}
+
+# +----------------------------------------------------------------------------+
+# | Runs NodeJS using the hermetic node toolchain.                             |
+# +----------------------------------------------------------------------------+
+template("node_bin") {
+  action(target_name) {
+    forward_variables_from(invoker,
+                           [
+                             "inputs",
+                             "outputs",
+                             "depfile",
+                           ])
+    deps = [ ":node_modules" ]
+    if (defined(invoker.deps)) {
+      deps += invoker.deps
+    }
+    script = "../../gn/standalone/build_tool_wrapper.py"
+    _node_cmd = invoker.node_cmd
+    args = []
+    if (defined(invoker.suppress_stdout) && invoker.suppress_stdout) {
+      args += [ "--suppress_stdout" ]
+    }
+    if (defined(invoker.suppress_stderr) && invoker.suppress_stderr) {
+      args += [ "--suppress_stderr" ]
+    }
+    args += [
+              "--path=$nodejs_bin",
+              "node",
+              rebase_path("node_modules/.bin/$_node_cmd", root_build_dir),
+            ] + invoker.args
+  }
+}
+
+# +----------------------------------------------------------------------------+
+# | Installs the node modules secified in package.json                         |
+# +----------------------------------------------------------------------------+
+action("node_modules") {
+  script = "../../gn/standalone/build_tool_wrapper.py"
+  stamp_file = "$dist_dir/.$target_name.stamp"
+  cur_dir = rebase_path(".", root_build_dir)
+  args = [
+    "--stamp",
+    rebase_path(stamp_file, root_build_dir),
+    "--chdir=$cur_dir",
+    "--path=$nodejs_bin",
+    "npm",
+    "install",
+  ]
+  inputs = [
+    "package.json",
+    "package-lock.json",
+  ]
+  outputs = [ stamp_file ]
+}
+
+copy("assets_dist") {
+  sources = [
+    "node_modules/highlight.js/styles/tomorrow-night.css",
+    "node_modules/mermaid/dist/mermaid.min.js",
+    "src/assets/analysis.png",
+    "src/assets/app_tracing.png",
+    "src/assets/brand.png",
+    "src/assets/favicon.png",
+    "src/assets/home.png",
+    "src/assets/script.js",
+    "src/assets/sprite.png",
+    "src/assets/sys_profiling.png",
+    "src/assets/ui.png",
+  ]
+  outputs = [ "$dist_dir/assets/{{source_file_part}}" ]
+}
+
+node_bin("scss") {
+  inputs = [ "src/assets/style.scss" ]
+  outputs = [ "$dist_dir/assets/style.css" ]
+  node_cmd = "node-sass"
+  args = [
+    "--quiet",
+    rebase_path(inputs[0], root_build_dir),
+    rebase_path(outputs[0], root_build_dir),
+  ]
+}
+
+# +----------------------------------------------------------------------------+
+# | Markdown generation                                                        |
+# +----------------------------------------------------------------------------+
+
+html_templates = [
+  "src/template_header.html",
+  "src/template_footer.html",
+  "src/template_index.html",
+  "src/template_markdown.html",
+]
+
+# Renderers a markdown file into a HTML.
+# Args:
+#  markdown:        [in]  The source markdown file.
+#  html_template:   [in]  HTML template (optional).
+#  out_html:        [out] The generated HTML.
+template("md_to_html") {
+  action(target_name) {
+    js_src = "src/markdown_render.js"
+    script = "../../gn/standalone/build_tool_wrapper.py"
+    outputs = [ invoker.out_html ]
+    inputs = [ js_src ] + html_templates
+    args = [
+      "--path=$nodejs_bin",
+      "node",
+      rebase_path(js_src, root_build_dir),
+      "--odir",
+      rebase_path(dist_dir, root_build_dir),
+      "-o",
+      rebase_path(outputs[0], root_build_dir),
+    ]
+    if (defined(invoker.html_template)) {
+      args += [
+        "-t",
+        invoker.html_template,
+      ]
+      inputs += [ invoker.html_template ]
+    }
+    if (defined(invoker.markdown)) {
+      args += [
+        "-i",
+        rebase_path(invoker.markdown, root_build_dir),
+      ]
+      inputs += [ invoker.markdown ]
+    }
+    if (defined(invoker.deps)) {
+      deps = invoker.deps
+    }
+  }
+}
+
+# Generates a .md file from a .proto.
+# Args:
+#  proto_src: [in]
+#  proto_root_type: [in]
+#  markdown_out: [in]
+template("gen_proto_reference_md") {
+  action(target_name) {
+    js_src = "src/gen_proto_reference.js"
+    script = "../../gn/standalone/build_tool_wrapper.py"
+    inputs = [
+      invoker.proto_src,
+      js_src,
+    ]
+    outputs = [ invoker.markdown_out ]
+    args = [
+      "--path=$nodejs_bin",
+      "node",
+      rebase_path(js_src, root_build_dir),
+      "-o",
+      rebase_path(invoker.markdown_out, root_build_dir),
+      "-i",
+      rebase_path(invoker.proto_src, root_build_dir),
+      "-p",
+      invoker.proto_root_type,
+    ]
+  }
+}
+
+# +----------------------------------------------------------------------------+
+# | /reference/ auto-generated docs                                            |
+# +----------------------------------------------------------------------------+
+
+# Generates a .html from a proto (proto -> md -> html).
+template("gen_proto_reference_html") {
+  intermediate_md = "$target_out_dir/$target_name.md"
+  gen_proto_reference_md("${target_name}_md") {
+    proto_src = invoker.proto_src
+    proto_root_type = invoker.proto_root_type
+    markdown_out = intermediate_md
+  }
+  md_to_html(target_name) {
+    deps = [
+      ":${target_name}_md",
+      ":navmap",
+    ]
+    markdown = intermediate_md
+    out_html = invoker.out_html
+    html_template = rebase_path("src/template_markdown.html", root_build_dir)
+  }
+}
+
+gen_proto_reference_html("trace_config_reference") {
+  proto_src = "../../protos/perfetto/config/trace_config.proto"
+  proto_root_type = "perfetto.protos.TraceConfig"
+  out_html = "$dist_dir/docs/reference/trace-config-proto"
+}
+
+gen_proto_reference_html("trace_packet_reference") {
+  proto_src = "../../protos/perfetto/trace/trace_packet.proto"
+  proto_root_type = "perfetto.protos.TracePacket"
+  out_html = "$dist_dir/docs/reference/trace-packet-proto"
+}
+
+# Generates the HTML for the sidebar.
+md_to_html("navmap") {
+  markdown = "../../docs/toc.md"
+  out_html = "$dist_dir/docs/_nav.html"
+}
+
+# Generates the root index.html.
+md_to_html("index_dist") {
+  html_template = rebase_path("src/template_index.html", root_build_dir)
+  out_html = "$dist_dir/index.html"
+}
+
+tp_tables_list = "$target_out_dir/tables.deps"
+exec_script("../../gn/standalone/glob.py",
+            [
+              "--root=" + rebase_path("../../src/trace_processor/tables",
+                                      root_build_dir),
+              "--filter=*.h",
+              "--output=" + rebase_path(tp_tables_list),
+            ])
+
+action("sql_tables_reference_md") {
+  js_src = "src/gen_sql_tables_reference.js"
+  script = "../../gn/standalone/build_tool_wrapper.py"
+  tp_table_hdrs = read_file(tp_tables_list, "list lines")
+  inputs = tp_table_hdrs + [ js_src ]
+  outputs = [ "$target_out_dir/sql-tables.autogen" ]
+  args = [
+    "--path=$nodejs_bin",
+    "node",
+    rebase_path(js_src, root_build_dir),
+    "-o",
+    rebase_path(outputs[0], root_build_dir),
+  ]
+  foreach(table_hdr, tp_table_hdrs) {
+    args += [
+      "-i",
+      table_hdr,
+    ]
+  }
+}
+
+md_to_html("sql_tables_reference") {
+  deps = [
+    ":navmap",
+    ":sql_tables_reference_md",
+  ]
+  markdown = "$target_out_dir/sql-tables.autogen"
+  out_html = "$dist_dir/docs/analysis/sql-tables"
+  html_template = rebase_path("src/template_markdown.html", root_build_dir)
+}
+
+action("stats_reference_md") {
+  js_src = "src/gen_stats_reference.js"
+  script = "../../gn/standalone/build_tool_wrapper.py"
+  input = "../../src/trace_processor/storage/stats.h"
+  inputs = [
+    input,
+    js_src,
+  ]
+  outputs = [ "$target_out_dir/sql-stats.autogen" ]
+  args = [
+    "--path=$nodejs_bin",
+    "node",
+    rebase_path(js_src, root_build_dir),
+    "-o",
+    rebase_path(outputs[0], root_build_dir),
+    "-i",
+    "../../src/trace_processor/storage/stats.h",
+  ]
+}
+
+md_to_html("stats_reference") {
+  deps = [
+    ":navmap",
+    ":stats_reference_md",
+  ]
+  markdown = "$target_out_dir/sql-stats.autogen"
+  out_html = "$dist_dir/docs/analysis/sql-stats"
+  html_template = rebase_path("src/template_markdown.html", root_build_dir)
+}
+
+# Generates a HTML file for each //docs/**/*.md.
+md_file_list = "$target_out_dir/docs.deps"
+exec_script("../../gn/standalone/glob.py",
+            [
+              "--root=" + rebase_path("../../docs", root_build_dir),
+              "--filter=*.md",
+              "--output=" + rebase_path(md_file_list),
+            ],
+            "",
+            [ "../../docs/toc.md" ])
+all_md_targets = []
+foreach(source, read_file(md_file_list, "list lines")) {
+  target_name = "gen_$source"
+  all_md_targets += [ ":$target_name" ]
+  md_to_html(target_name) {
+    deps = [ ":navmap" ]
+    base_name = string_replace(rebase_path(source, "../../docs"), ".md", "")
+    if (base_name == "README") {
+      base_name = "index.html"
+    }
+    markdown = source
+    out_html = "$dist_dir/docs/$base_name"
+    html_template = rebase_path("src/template_markdown.html", root_build_dir)
+  }
+}
+
+group("markdown") {
+  deps = all_md_targets
+  deps += [
+    ":sql_tables_reference",
+    ":stats_reference",
+    ":trace_config_reference",
+    ":trace_packet_reference",
+  ]
+}
diff --git a/infra/perfetto.dev/app.yaml b/infra/perfetto.dev/app.yaml
new file mode 100644
index 0000000..d20e18f
--- /dev/null
+++ b/infra/perfetto.dev/app.yaml
@@ -0,0 +1,45 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+runtime: python37
+default_expiration: "5m"
+handlers:
+- url: /$
+  static_files: dist/index.html
+  upload: dist/index.html
+  secure: always
+  redirect_http_response_code: 301
+- url: /docs/?$
+  static_files: dist/docs/index.html
+  upload: dist/docs/index.html
+  mime_type: text/html
+  secure: always
+  redirect_http_response_code: 301
+- url: /docs/(.*\.svg)$
+  static_files: dist/docs/\1
+  mime_type: image/svg+xml
+  upload: dist/docs/.*\.svg$
+- url: /docs/(.*/[A-Za-z0-9-_]+)$
+  static_files: dist/docs/\1
+  mime_type: text/html
+  upload: dist/docs/.*/[A-Za-z0-9-_]+$
+  secure: always
+  redirect_http_response_code: 301
+- url: /assets/
+  static_dir: dist/assets
+  expiration: 60m
+- url: /
+  static_dir: dist/
+  secure: always
+  redirect_http_response_code: 301
diff --git a/infra/perfetto.dev/deploy b/infra/perfetto.dev/deploy
new file mode 100755
index 0000000..0b8336b
--- /dev/null
+++ b/infra/perfetto.dev/deploy
@@ -0,0 +1,80 @@
+#!/bin/bash
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e
+
+readonly CUR_DIR="$(cd -P ${BASH_SOURCE[0]%/*}; pwd)"
+readonly ROOT_DIR=$(dirname $(dirname "$CUR_DIR"))
+readonly NODE_DIR="$ROOT_DIR/buildtools/nodejs/bin"
+readonly WRAPPER="$ROOT_DIR/gn/standalone/build_tool_wrapper.py"
+
+cd $CUR_DIR
+
+function echo_and_do {
+  echo $@
+  $@
+}
+
+function npm {
+  echo_and_do python "$WRAPPER" --path $NODE_DIR npm $@
+}
+
+CLEAN_OUT_DIR=true
+DEPLOY_PROD=false
+DEPLOY_STAGING=false
+
+while [[ $# -gt 0 ]]; do
+  key="$1"
+  case $key in
+      --no-clean)
+      CLEAN_OUT_DIR=false
+      shift
+      ;;
+      --prod)
+      DEPLOY_PROD=true
+      shift
+      ;;
+      --staging)
+      DEPLOY_STAGING=true
+      shift
+      ;;
+      -h|--help)
+      echo "Usage: $0 [--no-clean] [--prod] [--staging]"
+      echo "    --no-clean  Don't remove the out directory"
+      echo "    --prod      Deploy prod version (implies --release)"
+      echo "    --staging   Deploy staging version"
+      echo "    -h|--help   Display this message"
+      exit 0
+      shift
+      ;;
+  esac
+done
+
+if [ "$CLEAN_OUT_DIR" = true ]; then
+  npm run clean
+fi
+
+npm run build
+
+if [ "$DEPLOY_PROD" = true ]; then
+  echo_and_do gcloud app deploy app.yaml --project perfetto-site \
+      -v prod --promote --stop-previous-version
+elif [ "$DEPLOY_STAGING" = true ]; then
+  echo_and_do gcloud app deploy app.yaml --project perfetto-site \
+      -v staging --no-promote --stop-previous-version
+else
+  echo_and_do gcloud app deploy app.yaml --project perfetto-site \
+      -v $USER --no-promote --stop-previous-version
+fi
diff --git a/infra/perfetto.dev/dist b/infra/perfetto.dev/dist
new file mode 120000
index 0000000..eac3128
--- /dev/null
+++ b/infra/perfetto.dev/dist
@@ -0,0 +1 @@
+../../out/perfetto.dev/site
\ No newline at end of file
diff --git a/infra/perfetto.dev/package-lock.json b/infra/perfetto.dev/package-lock.json
new file mode 100644
index 0000000..9f6302e
--- /dev/null
+++ b/infra/perfetto.dev/package-lock.json
@@ -0,0 +1,2770 @@
+{
+  "name": "perfetto-dev",
+  "version": "1.0.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "@blakeembrey/deque": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/@blakeembrey/deque/-/deque-1.0.5.tgz",
+      "integrity": "sha512-6xnwtvp9DY1EINIKdTfvfeAtCYw4OqBZJhtiqkT3ivjnEfa25VQ3TsKvaFfKm8MyGIEfE95qLe+bNEt3nB0Ylg==",
+      "dev": true
+    },
+    "@braintree/sanitize-url": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-3.1.0.tgz",
+      "integrity": "sha512-GcIY79elgB+azP74j8vqkiXz8xLFfIzbQJdlwOPisgbKT00tviJQuEghOXSMVxJ00HoYJbGswr4kcllUc4xCcg=="
+    },
+    "@protobufjs/aspromise": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+      "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=",
+      "dev": true
+    },
+    "@protobufjs/base64": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+      "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
+      "dev": true
+    },
+    "@protobufjs/codegen": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+      "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
+      "dev": true
+    },
+    "@protobufjs/eventemitter": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+      "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=",
+      "dev": true
+    },
+    "@protobufjs/fetch": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+      "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=",
+      "dev": true,
+      "requires": {
+        "@protobufjs/aspromise": "^1.1.1",
+        "@protobufjs/inquire": "^1.1.0"
+      }
+    },
+    "@protobufjs/float": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+      "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=",
+      "dev": true
+    },
+    "@protobufjs/inquire": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+      "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=",
+      "dev": true
+    },
+    "@protobufjs/path": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+      "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=",
+      "dev": true
+    },
+    "@protobufjs/pool": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+      "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=",
+      "dev": true
+    },
+    "@protobufjs/utf8": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+      "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=",
+      "dev": true
+    },
+    "@types/color-name": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
+      "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
+      "dev": true
+    },
+    "@types/long": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
+      "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==",
+      "dev": true
+    },
+    "@types/node": {
+      "version": "13.13.1",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.1.tgz",
+      "integrity": "sha512-uysqysLJ+As9jqI5yqjwP3QJrhOcUwBjHUlUxPxjbplwKoILvXVsmYWEhfmAQlrPfbRZmhJB007o4L9sKqtHqQ==",
+      "dev": true
+    },
+    "abbrev": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+      "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+      "dev": true
+    },
+    "ajv": {
+      "version": "6.11.0",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz",
+      "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==",
+      "dev": true,
+      "requires": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      }
+    },
+    "amdefine": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
+      "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
+      "dev": true
+    },
+    "ansi-regex": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+      "dev": true
+    },
+    "ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+      "dev": true,
+      "requires": {
+        "color-convert": "^1.9.0"
+      }
+    },
+    "anymatch": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
+      "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
+      "dev": true,
+      "requires": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      }
+    },
+    "aproba": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+      "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
+      "dev": true
+    },
+    "are-we-there-yet": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
+      "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
+      "dev": true,
+      "requires": {
+        "delegates": "^1.0.0",
+        "readable-stream": "^2.0.6"
+      }
+    },
+    "array-find-index": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
+      "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
+      "dev": true
+    },
+    "arrify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
+      "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
+      "dev": true
+    },
+    "asn1": {
+      "version": "0.2.4",
+      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
+      "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
+      "dev": true,
+      "requires": {
+        "safer-buffer": "~2.1.0"
+      }
+    },
+    "assert-plus": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+      "dev": true
+    },
+    "async-foreach": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz",
+      "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=",
+      "dev": true
+    },
+    "asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+      "dev": true
+    },
+    "at-least-node": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
+      "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
+      "dev": true
+    },
+    "aws-sign2": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+      "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
+      "dev": true
+    },
+    "aws4": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
+      "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==",
+      "dev": true
+    },
+    "balanced-match": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+      "dev": true
+    },
+    "bcrypt-pbkdf": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+      "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+      "dev": true,
+      "requires": {
+        "tweetnacl": "^0.14.3"
+      }
+    },
+    "binary-extensions": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz",
+      "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==",
+      "dev": true
+    },
+    "block-stream": {
+      "version": "0.0.9",
+      "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
+      "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
+      "dev": true,
+      "requires": {
+        "inherits": "~2.0.0"
+      }
+    },
+    "brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "requires": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "dev": true,
+      "requires": {
+        "fill-range": "^7.0.1"
+      }
+    },
+    "buffer-from": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+      "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
+    },
+    "camel-case": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz",
+      "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=",
+      "requires": {
+        "no-case": "^2.2.0",
+        "upper-case": "^1.1.1"
+      }
+    },
+    "camelcase": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
+      "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
+      "dev": true
+    },
+    "camelcase-keys": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
+      "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
+      "dev": true,
+      "requires": {
+        "camelcase": "^2.0.0",
+        "map-obj": "^1.0.0"
+      }
+    },
+    "caseless": {
+      "version": "0.12.0",
+      "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+      "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
+      "dev": true
+    },
+    "chalk": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
+      },
+      "dependencies": {
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        }
+      }
+    },
+    "chokidar": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz",
+      "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==",
+      "dev": true,
+      "requires": {
+        "anymatch": "~3.1.1",
+        "braces": "~3.0.2",
+        "fsevents": "~2.1.2",
+        "glob-parent": "~5.1.0",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.3.0"
+      }
+    },
+    "clean-css": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz",
+      "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==",
+      "requires": {
+        "source-map": "~0.6.0"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+        }
+      }
+    },
+    "cliui": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
+      "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
+      "dev": true,
+      "requires": {
+        "string-width": "^1.0.1",
+        "strip-ansi": "^3.0.1",
+        "wrap-ansi": "^2.0.0"
+      }
+    },
+    "code-point-at": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+      "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
+      "dev": true
+    },
+    "color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "dev": true,
+      "requires": {
+        "color-name": "1.1.3"
+      }
+    },
+    "color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+      "dev": true
+    },
+    "combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "dev": true,
+      "requires": {
+        "delayed-stream": "~1.0.0"
+      }
+    },
+    "commander": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.6.0.tgz",
+      "integrity": "sha1-nfflL7Kgyw+4kFjugMMQQiXzfh0="
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+      "dev": true
+    },
+    "concurrently": {
+      "version": "3.6.1",
+      "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-3.6.1.tgz",
+      "integrity": "sha512-/+ugz+gwFSEfTGUxn0KHkY+19XPRTXR8+7oUK/HxgiN1n7FjeJmkrbSiXAJfyQ0zORgJYPaenmymwon51YXH9Q==",
+      "dev": true,
+      "requires": {
+        "chalk": "^2.4.1",
+        "commander": "2.6.0",
+        "date-fns": "^1.23.0",
+        "lodash": "^4.5.1",
+        "read-pkg": "^3.0.0",
+        "rx": "2.3.24",
+        "spawn-command": "^0.0.2-1",
+        "supports-color": "^3.2.3",
+        "tree-kill": "^1.1.0"
+      }
+    },
+    "console-control-strings": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+      "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
+      "dev": true
+    },
+    "core-util-is": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+      "dev": true
+    },
+    "cross-spawn": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz",
+      "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=",
+      "dev": true,
+      "requires": {
+        "lru-cache": "^4.0.1",
+        "which": "^1.2.9"
+      }
+    },
+    "crypto-random-string": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-3.2.0.tgz",
+      "integrity": "sha512-8vPu5bsKaq2uKRy3OL7h1Oo7RayAWB8sYexLKAqvCXVib8SxgbmoF1IN4QMKjBv8uI8mp5gPPMbiRah25GMrVQ==",
+      "requires": {
+        "type-fest": "^0.8.1"
+      }
+    },
+    "css-b64-images": {
+      "version": "0.2.5",
+      "resolved": "https://registry.npmjs.org/css-b64-images/-/css-b64-images-0.2.5.tgz",
+      "integrity": "sha1-QgBdgyBLK0pdk7axpWRBM7WSegI="
+    },
+    "currently-unhandled": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
+      "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
+      "dev": true,
+      "requires": {
+        "array-find-index": "^1.0.1"
+      }
+    },
+    "d3": {
+      "version": "5.16.0",
+      "resolved": "https://registry.npmjs.org/d3/-/d3-5.16.0.tgz",
+      "integrity": "sha512-4PL5hHaHwX4m7Zr1UapXW23apo6pexCgdetdJ5kTmADpG/7T9Gkxw0M0tf/pjoB63ezCCm0u5UaFYy2aMt0Mcw==",
+      "requires": {
+        "d3-array": "1",
+        "d3-axis": "1",
+        "d3-brush": "1",
+        "d3-chord": "1",
+        "d3-collection": "1",
+        "d3-color": "1",
+        "d3-contour": "1",
+        "d3-dispatch": "1",
+        "d3-drag": "1",
+        "d3-dsv": "1",
+        "d3-ease": "1",
+        "d3-fetch": "1",
+        "d3-force": "1",
+        "d3-format": "1",
+        "d3-geo": "1",
+        "d3-hierarchy": "1",
+        "d3-interpolate": "1",
+        "d3-path": "1",
+        "d3-polygon": "1",
+        "d3-quadtree": "1",
+        "d3-random": "1",
+        "d3-scale": "2",
+        "d3-scale-chromatic": "1",
+        "d3-selection": "1",
+        "d3-shape": "1",
+        "d3-time": "1",
+        "d3-time-format": "2",
+        "d3-timer": "1",
+        "d3-transition": "1",
+        "d3-voronoi": "1",
+        "d3-zoom": "1"
+      }
+    },
+    "d3-array": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
+      "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
+    },
+    "d3-axis": {
+      "version": "1.0.12",
+      "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.12.tgz",
+      "integrity": "sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ=="
+    },
+    "d3-brush": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.1.5.tgz",
+      "integrity": "sha512-rEaJ5gHlgLxXugWjIkolTA0OyMvw8UWU1imYXy1v642XyyswmI1ybKOv05Ft+ewq+TFmdliD3VuK0pRp1VT/5A==",
+      "requires": {
+        "d3-dispatch": "1",
+        "d3-drag": "1",
+        "d3-interpolate": "1",
+        "d3-selection": "1",
+        "d3-transition": "1"
+      }
+    },
+    "d3-chord": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.6.tgz",
+      "integrity": "sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==",
+      "requires": {
+        "d3-array": "1",
+        "d3-path": "1"
+      }
+    },
+    "d3-collection": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz",
+      "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A=="
+    },
+    "d3-color": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz",
+      "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q=="
+    },
+    "d3-contour": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-1.3.2.tgz",
+      "integrity": "sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==",
+      "requires": {
+        "d3-array": "^1.1.1"
+      }
+    },
+    "d3-dispatch": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz",
+      "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA=="
+    },
+    "d3-drag": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.5.tgz",
+      "integrity": "sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w==",
+      "requires": {
+        "d3-dispatch": "1",
+        "d3-selection": "1"
+      }
+    },
+    "d3-dsv": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.2.0.tgz",
+      "integrity": "sha512-9yVlqvZcSOMhCYzniHE7EVUws7Fa1zgw+/EAV2BxJoG3ME19V6BQFBwI855XQDsxyOuG7NibqRMTtiF/Qup46g==",
+      "requires": {
+        "commander": "2",
+        "iconv-lite": "0.4",
+        "rw": "1"
+      }
+    },
+    "d3-ease": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.6.tgz",
+      "integrity": "sha512-SZ/lVU7LRXafqp7XtIcBdxnWl8yyLpgOmzAk0mWBI9gXNzLDx5ybZgnRbH9dN/yY5tzVBqCQ9avltSnqVwessQ=="
+    },
+    "d3-fetch": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-1.1.2.tgz",
+      "integrity": "sha512-S2loaQCV/ZeyTyIF2oP8D1K9Z4QizUzW7cWeAOAS4U88qOt3Ucf6GsmgthuYSdyB2HyEm4CeGvkQxWsmInsIVA==",
+      "requires": {
+        "d3-dsv": "1"
+      }
+    },
+    "d3-force": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz",
+      "integrity": "sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==",
+      "requires": {
+        "d3-collection": "1",
+        "d3-dispatch": "1",
+        "d3-quadtree": "1",
+        "d3-timer": "1"
+      }
+    },
+    "d3-format": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.4.tgz",
+      "integrity": "sha512-TWks25e7t8/cqctxCmxpUuzZN11QxIA7YrMbram94zMQ0PXjE4LVIMe/f6a4+xxL8HQ3OsAFULOINQi1pE62Aw=="
+    },
+    "d3-geo": {
+      "version": "1.12.0",
+      "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.12.0.tgz",
+      "integrity": "sha512-NalZVW+6/SpbKcnl+BCO67m8gX+nGeJdo6oGL9H6BRUGUL1e+AtPcP4vE4TwCQ/gl8y5KE7QvBzrLn+HsKIl+w==",
+      "requires": {
+        "d3-array": "1"
+      }
+    },
+    "d3-hierarchy": {
+      "version": "1.1.9",
+      "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz",
+      "integrity": "sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ=="
+    },
+    "d3-interpolate": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz",
+      "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==",
+      "requires": {
+        "d3-color": "1"
+      }
+    },
+    "d3-path": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz",
+      "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="
+    },
+    "d3-polygon": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.6.tgz",
+      "integrity": "sha512-k+RF7WvI08PC8reEoXa/w2nSg5AUMTi+peBD9cmFc+0ixHfbs4QmxxkarVal1IkVkgxVuk9JSHhJURHiyHKAuQ=="
+    },
+    "d3-quadtree": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.7.tgz",
+      "integrity": "sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA=="
+    },
+    "d3-random": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.2.tgz",
+      "integrity": "sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ=="
+    },
+    "d3-scale": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz",
+      "integrity": "sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==",
+      "requires": {
+        "d3-array": "^1.2.0",
+        "d3-collection": "1",
+        "d3-format": "1",
+        "d3-interpolate": "1",
+        "d3-time": "1",
+        "d3-time-format": "2"
+      }
+    },
+    "d3-scale-chromatic": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-1.5.0.tgz",
+      "integrity": "sha512-ACcL46DYImpRFMBcpk9HhtIyC7bTBR4fNOPxwVSl0LfulDAwyiHyPOTqcDG1+t5d4P9W7t/2NAuWu59aKko/cg==",
+      "requires": {
+        "d3-color": "1",
+        "d3-interpolate": "1"
+      }
+    },
+    "d3-selection": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.1.tgz",
+      "integrity": "sha512-BTIbRjv/m5rcVTfBs4AMBLKs4x8XaaLkwm28KWu9S2vKNqXkXt2AH2Qf0sdPZHjFxcWg/YL53zcqAz+3g4/7PA=="
+    },
+    "d3-shape": {
+      "version": "1.3.7",
+      "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz",
+      "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==",
+      "requires": {
+        "d3-path": "1"
+      }
+    },
+    "d3-time": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz",
+      "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA=="
+    },
+    "d3-time-format": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.2.3.tgz",
+      "integrity": "sha512-RAHNnD8+XvC4Zc4d2A56Uw0yJoM7bsvOlJR33bclxq399Rak/b9bhvu/InjxdWhPtkgU53JJcleJTGkNRnN6IA==",
+      "requires": {
+        "d3-time": "1"
+      }
+    },
+    "d3-timer": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz",
+      "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw=="
+    },
+    "d3-transition": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.3.2.tgz",
+      "integrity": "sha512-sc0gRU4PFqZ47lPVHloMn9tlPcv8jxgOQg+0zjhfZXMQuvppjG6YuwdMBE0TuqCZjeJkLecku/l9R0JPcRhaDA==",
+      "requires": {
+        "d3-color": "1",
+        "d3-dispatch": "1",
+        "d3-ease": "1",
+        "d3-interpolate": "1",
+        "d3-selection": "^1.1.0",
+        "d3-timer": "1"
+      }
+    },
+    "d3-voronoi": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz",
+      "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg=="
+    },
+    "d3-zoom": {
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.8.3.tgz",
+      "integrity": "sha512-VoLXTK4wvy1a0JpH2Il+F2CiOhVu7VRXWF5M/LroMIh3/zBAC3WAt7QoIvPibOavVo20hN6/37vwAsdBejLyKQ==",
+      "requires": {
+        "d3-dispatch": "1",
+        "d3-drag": "1",
+        "d3-interpolate": "1",
+        "d3-selection": "1",
+        "d3-transition": "1"
+      }
+    },
+    "dagre": {
+      "version": "0.8.5",
+      "resolved": "https://registry.npmjs.org/dagre/-/dagre-0.8.5.tgz",
+      "integrity": "sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==",
+      "requires": {
+        "graphlib": "^2.1.8",
+        "lodash": "^4.17.15"
+      }
+    },
+    "dagre-d3": {
+      "version": "0.6.4",
+      "resolved": "https://registry.npmjs.org/dagre-d3/-/dagre-d3-0.6.4.tgz",
+      "integrity": "sha512-e/6jXeCP7/ptlAM48clmX4xTZc5Ek6T6kagS7Oz2HrYSdqcLZFLqpAfh7ldbZRFfxCZVyh61NEPR08UQRVxJzQ==",
+      "requires": {
+        "d3": "^5.14",
+        "dagre": "^0.8.5",
+        "graphlib": "^2.1.8",
+        "lodash": "^4.17.15"
+      }
+    },
+    "dashdash": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+      "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+      "dev": true,
+      "requires": {
+        "assert-plus": "^1.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
+    },
+    "debug": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+      "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+      "requires": {
+        "ms": "^2.1.1"
+      }
+    },
+    "decamelize": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+      "dev": true
+    },
+    "delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+      "dev": true
+    },
+    "delegates": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+      "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
+      "dev": true
+    },
+    "ecc-jsbn": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+      "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+      "dev": true,
+      "requires": {
+        "jsbn": "~0.1.0",
+        "safer-buffer": "^2.1.0"
+      }
+    },
+    "ejs": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.0.1.tgz",
+      "integrity": "sha512-cuIMtJwxvzumSAkqaaoGY/L6Fc/t6YvoP9/VIaK0V/CyqKLEQ8sqODmYfy/cjXEdZ9+OOL8TecbJu+1RsofGDw==",
+      "dev": true
+    },
+    "emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "dev": true
+    },
+    "error-ex": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+      "dev": true,
+      "requires": {
+        "is-arrayish": "^0.2.1"
+      }
+    },
+    "escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+      "dev": true
+    },
+    "escaper": {
+      "version": "2.5.3",
+      "resolved": "https://registry.npmjs.org/escaper/-/escaper-2.5.3.tgz",
+      "integrity": "sha512-QGb9sFxBVpbzMggrKTX0ry1oiI4CSDAl9vIL702hzl1jGW8VZs7qfqTRX7WDOjoNDoEVGcEtu1ZOQgReSfT2kQ=="
+    },
+    "extend": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+      "dev": true
+    },
+    "extsprintf": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+      "dev": true
+    },
+    "fast-deep-equal": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
+      "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==",
+      "dev": true
+    },
+    "fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+      "dev": true
+    },
+    "fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "dev": true,
+      "requires": {
+        "to-regex-range": "^5.0.1"
+      }
+    },
+    "find-up": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+      "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+      "dev": true,
+      "requires": {
+        "path-exists": "^2.0.0",
+        "pinkie-promise": "^2.0.0"
+      }
+    },
+    "forever-agent": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+      "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+      "dev": true
+    },
+    "form-data": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+      "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+      "dev": true,
+      "requires": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.6",
+        "mime-types": "^2.1.12"
+      }
+    },
+    "fs-extra": {
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.0.tgz",
+      "integrity": "sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g==",
+      "dev": true,
+      "requires": {
+        "at-least-node": "^1.0.0",
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^1.0.0"
+      }
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+      "dev": true
+    },
+    "fsevents": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz",
+      "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==",
+      "dev": true,
+      "optional": true
+    },
+    "fstream": {
+      "version": "1.0.12",
+      "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
+      "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.1.2",
+        "inherits": "~2.0.0",
+        "mkdirp": ">=0.5 0",
+        "rimraf": "2"
+      }
+    },
+    "gauge": {
+      "version": "2.7.4",
+      "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
+      "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
+      "dev": true,
+      "requires": {
+        "aproba": "^1.0.3",
+        "console-control-strings": "^1.0.0",
+        "has-unicode": "^2.0.0",
+        "object-assign": "^4.1.0",
+        "signal-exit": "^3.0.0",
+        "string-width": "^1.0.1",
+        "strip-ansi": "^3.0.1",
+        "wide-align": "^1.1.0"
+      }
+    },
+    "gaze": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz",
+      "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==",
+      "dev": true,
+      "requires": {
+        "globule": "^1.0.0"
+      }
+    },
+    "get-caller-file": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
+      "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
+      "dev": true
+    },
+    "get-stdin": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
+      "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
+      "dev": true
+    },
+    "getpass": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+      "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+      "dev": true,
+      "requires": {
+        "assert-plus": "^1.0.0"
+      }
+    },
+    "glob": {
+      "version": "7.1.6",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+      "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+      "dev": true,
+      "requires": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.4",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      }
+    },
+    "glob-parent": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz",
+      "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==",
+      "dev": true,
+      "requires": {
+        "is-glob": "^4.0.1"
+      }
+    },
+    "globule": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.0.tgz",
+      "integrity": "sha512-YlD4kdMqRCQHrhVdonet4TdRtv1/sZKepvoxNT4Nrhrp5HI8XFfc8kFlGlBn2myBo80aGp8Eft259mbcUJhgSg==",
+      "dev": true,
+      "requires": {
+        "glob": "~7.1.1",
+        "lodash": "~4.17.10",
+        "minimatch": "~3.0.2"
+      }
+    },
+    "graceful-fs": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
+      "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
+      "dev": true
+    },
+    "graphlib": {
+      "version": "2.1.8",
+      "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz",
+      "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==",
+      "requires": {
+        "lodash": "^4.17.15"
+      }
+    },
+    "har-schema": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
+      "dev": true
+    },
+    "har-validator": {
+      "version": "5.1.3",
+      "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
+      "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
+      "dev": true,
+      "requires": {
+        "ajv": "^6.5.5",
+        "har-schema": "^2.0.0"
+      }
+    },
+    "has-ansi": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+      "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+      "dev": true,
+      "requires": {
+        "ansi-regex": "^2.0.0"
+      }
+    },
+    "has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+      "dev": true
+    },
+    "has-unicode": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+      "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
+      "dev": true
+    },
+    "he": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+      "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
+    },
+    "highlight.js": {
+      "version": "9.18.1",
+      "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.1.tgz",
+      "integrity": "sha512-OrVKYz70LHsnCgmbXctv/bfuvntIKDz177h0Co37DQ5jamGZLVmoCVMtjMtNZY3X9DrCcKfklHPNeA0uPZhSJg==",
+      "dev": true
+    },
+    "hosted-git-info": {
+      "version": "2.8.5",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz",
+      "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==",
+      "dev": true
+    },
+    "html-minifier": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz",
+      "integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==",
+      "requires": {
+        "camel-case": "^3.0.0",
+        "clean-css": "^4.2.1",
+        "commander": "^2.19.0",
+        "he": "^1.2.0",
+        "param-case": "^2.1.1",
+        "relateurl": "^0.2.7",
+        "uglify-js": "^3.5.1"
+      },
+      "dependencies": {
+        "commander": {
+          "version": "2.20.3",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+          "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+        }
+      }
+    },
+    "http-signature": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+      "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+      "dev": true,
+      "requires": {
+        "assert-plus": "^1.0.0",
+        "jsprim": "^1.2.2",
+        "sshpk": "^1.7.0"
+      }
+    },
+    "iconv-lite": {
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "requires": {
+        "safer-buffer": ">= 2.1.2 < 3"
+      }
+    },
+    "ignore": {
+      "version": "5.1.4",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz",
+      "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==",
+      "dev": true
+    },
+    "in-publish": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz",
+      "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=",
+      "dev": true
+    },
+    "indent-string": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
+      "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
+      "dev": true,
+      "requires": {
+        "repeating": "^2.0.0"
+      }
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "dev": true,
+      "requires": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "dev": true
+    },
+    "invert-kv": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
+      "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
+      "dev": true
+    },
+    "is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+      "dev": true
+    },
+    "is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "dev": true,
+      "requires": {
+        "binary-extensions": "^2.0.0"
+      }
+    },
+    "is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+      "dev": true
+    },
+    "is-finite": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz",
+      "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==",
+      "dev": true
+    },
+    "is-fullwidth-code-point": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+      "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+      "dev": true,
+      "requires": {
+        "number-is-nan": "^1.0.0"
+      }
+    },
+    "is-glob": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+      "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+      "dev": true,
+      "requires": {
+        "is-extglob": "^2.1.1"
+      }
+    },
+    "is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true
+    },
+    "is-regexp": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz",
+      "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk="
+    },
+    "is-typedarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+      "dev": true
+    },
+    "is-utf8": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
+      "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
+      "dev": true
+    },
+    "isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+      "dev": true
+    },
+    "isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+      "dev": true
+    },
+    "isstream": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
+      "dev": true
+    },
+    "js-base64": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz",
+      "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==",
+      "dev": true
+    },
+    "jsbn": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+      "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+      "dev": true
+    },
+    "json-parse-better-errors": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+      "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+      "dev": true
+    },
+    "json-schema": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
+      "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
+      "dev": true
+    },
+    "json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true
+    },
+    "json-stringify-safe": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+      "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+      "dev": true
+    },
+    "jsonfile": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz",
+      "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.1.6",
+        "universalify": "^1.0.0"
+      }
+    },
+    "jsprim": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
+      "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+      "dev": true,
+      "requires": {
+        "assert-plus": "1.0.0",
+        "extsprintf": "1.3.0",
+        "json-schema": "0.2.3",
+        "verror": "1.10.0"
+      }
+    },
+    "lcid": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
+      "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
+      "dev": true,
+      "requires": {
+        "invert-kv": "^1.0.0"
+      }
+    },
+    "load-json-file": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
+      "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.1.2",
+        "parse-json": "^4.0.0",
+        "pify": "^3.0.0",
+        "strip-bom": "^3.0.0"
+      }
+    },
+    "locate-path": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+      "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+      "dev": true,
+      "requires": {
+        "p-locate": "^4.1.0"
+      }
+    },
+    "lodash": {
+      "version": "4.17.15",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
+      "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
+    },
+    "long": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+      "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
+      "dev": true
+    },
+    "loud-rejection": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
+      "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
+      "dev": true,
+      "requires": {
+        "currently-unhandled": "^0.4.1",
+        "signal-exit": "^3.0.0"
+      }
+    },
+    "lower-case": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
+      "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw="
+    },
+    "lru-cache": {
+      "version": "4.1.5",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+      "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+      "dev": true,
+      "requires": {
+        "pseudomap": "^1.0.2",
+        "yallist": "^2.1.2"
+      }
+    },
+    "map-obj": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
+      "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
+      "dev": true
+    },
+    "marked": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/marked/-/marked-0.8.0.tgz",
+      "integrity": "sha512-MyUe+T/Pw4TZufHkzAfDj6HarCBWia2y27/bhuYkTaiUnfDYFnCP3KUN+9oM7Wi6JA2rymtVYbQu3spE0GCmxQ==",
+      "dev": true
+    },
+    "meow": {
+      "version": "3.7.0",
+      "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
+      "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
+      "dev": true,
+      "requires": {
+        "camelcase-keys": "^2.0.0",
+        "decamelize": "^1.1.2",
+        "loud-rejection": "^1.0.0",
+        "map-obj": "^1.0.1",
+        "minimist": "^1.1.3",
+        "normalize-package-data": "^2.3.4",
+        "object-assign": "^4.0.1",
+        "read-pkg-up": "^1.0.1",
+        "redent": "^1.0.0",
+        "trim-newlines": "^1.0.0"
+      }
+    },
+    "mermaid": {
+      "version": "8.5.0",
+      "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-8.5.0.tgz",
+      "integrity": "sha512-fZf4GAzkqWuSwo5L+BmzaBwWPudHkxL3M/t1RmS9Dvc2mcnv6hdhMaeC7poARpHaSGwkpb74LL81qXj+vAsVBg==",
+      "requires": {
+        "@braintree/sanitize-url": "^3.1.0",
+        "crypto-random-string": "^3.0.1",
+        "d3": "^5.7.0",
+        "dagre": "^0.8.4",
+        "dagre-d3": "^0.6.4",
+        "graphlib": "^2.1.7",
+        "he": "^1.2.0",
+        "lodash": "^4.17.11",
+        "minify": "^4.1.1",
+        "moment-mini": "^2.22.1",
+        "scope-css": "^1.2.1"
+      }
+    },
+    "mime-db": {
+      "version": "1.43.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
+      "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==",
+      "dev": true
+    },
+    "mime-types": {
+      "version": "2.1.26",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
+      "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
+      "dev": true,
+      "requires": {
+        "mime-db": "1.43.0"
+      }
+    },
+    "minify": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/minify/-/minify-4.1.3.tgz",
+      "integrity": "sha512-ykuscavxivSmVpcCzsXmsVTukWYLUUtPhHj0w2ILvHDGqC+hsuTCihBn9+PJBd58JNvWTNg9132J9nrrI2anzA==",
+      "requires": {
+        "clean-css": "^4.1.6",
+        "css-b64-images": "~0.2.5",
+        "debug": "^4.1.0",
+        "html-minifier": "^4.0.0",
+        "terser": "^4.0.0",
+        "try-catch": "^2.0.0",
+        "try-to-catch": "^1.0.2"
+      }
+    },
+    "minimatch": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+      "dev": true,
+      "requires": {
+        "brace-expansion": "^1.1.7"
+      }
+    },
+    "minimist": {
+      "version": "1.2.0",
+      "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+      "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+      "dev": true
+    },
+    "mkdirp": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+      "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+      "dev": true,
+      "requires": {
+        "minimist": "0.0.8"
+      },
+      "dependencies": {
+        "minimist": {
+          "version": "0.0.8",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+          "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+          "dev": true
+        }
+      }
+    },
+    "moment-mini": {
+      "version": "2.24.0",
+      "resolved": "https://registry.npmjs.org/moment-mini/-/moment-mini-2.24.0.tgz",
+      "integrity": "sha512-9ARkWHBs+6YJIvrIp0Ik5tyTTtP9PoV0Ssu2Ocq5y9v8+NOOpWiRshAp8c4rZVWTOe+157on/5G+zj5pwIQFEQ=="
+    },
+    "ms": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+    },
+    "nan": {
+      "version": "2.14.0",
+      "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
+      "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
+      "dev": true
+    },
+    "nice-try": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+      "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+      "dev": true
+    },
+    "no-case": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz",
+      "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==",
+      "requires": {
+        "lower-case": "^1.1.1"
+      }
+    },
+    "node-gyp": {
+      "version": "3.8.0",
+      "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz",
+      "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==",
+      "dev": true,
+      "requires": {
+        "fstream": "^1.0.0",
+        "glob": "^7.0.3",
+        "graceful-fs": "^4.1.2",
+        "mkdirp": "^0.5.0",
+        "nopt": "2 || 3",
+        "npmlog": "0 || 1 || 2 || 3 || 4",
+        "osenv": "0",
+        "request": "^2.87.0",
+        "rimraf": "2",
+        "semver": "~5.3.0",
+        "tar": "^2.0.0",
+        "which": "1"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "5.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
+          "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
+          "dev": true
+        }
+      }
+    },
+    "node-sass": {
+      "version": "4.13.1",
+      "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.13.1.tgz",
+      "integrity": "sha512-TTWFx+ZhyDx1Biiez2nB0L3YrCZ/8oHagaDalbuBSlqXgUPsdkUSzJsVxeDO9LtPB49+Fh3WQl3slABo6AotNw==",
+      "dev": true,
+      "requires": {
+        "async-foreach": "^0.1.3",
+        "chalk": "^1.1.1",
+        "cross-spawn": "^3.0.0",
+        "gaze": "^1.0.0",
+        "get-stdin": "^4.0.1",
+        "glob": "^7.0.3",
+        "in-publish": "^2.0.0",
+        "lodash": "^4.17.15",
+        "meow": "^3.7.0",
+        "mkdirp": "^0.5.1",
+        "nan": "^2.13.2",
+        "node-gyp": "^3.8.0",
+        "npmlog": "^4.0.0",
+        "request": "^2.88.0",
+        "sass-graph": "^2.2.4",
+        "stdout-stream": "^1.4.0",
+        "true-case-path": "^1.0.2"
+      },
+      "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
+        }
+      }
+    },
+    "nopt": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
+      "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
+      "dev": true,
+      "requires": {
+        "abbrev": "1"
+      }
+    },
+    "normalize-package-data": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+      "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+      "dev": true,
+      "requires": {
+        "hosted-git-info": "^2.1.4",
+        "resolve": "^1.10.0",
+        "semver": "2 || 3 || 4 || 5",
+        "validate-npm-package-license": "^3.0.1"
+      }
+    },
+    "normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true
+    },
+    "npmlog": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
+      "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
+      "dev": true,
+      "requires": {
+        "are-we-there-yet": "~1.1.2",
+        "console-control-strings": "~1.1.0",
+        "gauge": "~2.7.3",
+        "set-blocking": "~2.0.0"
+      }
+    },
+    "number-is-nan": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+      "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
+      "dev": true
+    },
+    "oauth-sign": {
+      "version": "0.9.0",
+      "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+      "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+      "dev": true
+    },
+    "object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+      "dev": true
+    },
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "dev": true,
+      "requires": {
+        "wrappy": "1"
+      }
+    },
+    "onchange": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/onchange/-/onchange-6.1.0.tgz",
+      "integrity": "sha512-T0wvi3yzNd+Lut2ymJp2e6fTiob0TLrXnjqGaiK9MAFB8MYo/k/ZClx6ps7YhTtQ88dDm+hDHmtJXP1nJT5WNA==",
+      "dev": true,
+      "requires": {
+        "@blakeembrey/deque": "^1.0.3",
+        "arrify": "^2.0.0",
+        "chokidar": "^3.0.0",
+        "cross-spawn": "^6.0.0",
+        "ignore": "^5.1.4",
+        "minimist": "^1.2.0",
+        "supports-color": "^7.0.0",
+        "tree-kill": "^1.2.0"
+      },
+      "dependencies": {
+        "cross-spawn": {
+          "version": "6.0.5",
+          "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+          "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+          "dev": true,
+          "requires": {
+            "nice-try": "^1.0.4",
+            "path-key": "^2.0.1",
+            "semver": "^5.5.0",
+            "shebang-command": "^1.2.0",
+            "which": "^1.2.9"
+          }
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+          "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
+    },
+    "os-homedir": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+      "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
+      "dev": true
+    },
+    "os-locale": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
+      "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
+      "dev": true,
+      "requires": {
+        "lcid": "^1.0.0"
+      }
+    },
+    "os-tmpdir": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+      "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+      "dev": true
+    },
+    "osenv": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
+      "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
+      "dev": true,
+      "requires": {
+        "os-homedir": "^1.0.0",
+        "os-tmpdir": "^1.0.0"
+      }
+    },
+    "p-limit": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
+      "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
+      "dev": true,
+      "requires": {
+        "p-try": "^2.0.0"
+      }
+    },
+    "p-locate": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+      "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+      "dev": true,
+      "requires": {
+        "p-limit": "^2.2.0"
+      }
+    },
+    "p-try": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+      "dev": true
+    },
+    "param-case": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz",
+      "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=",
+      "requires": {
+        "no-case": "^2.2.0"
+      }
+    },
+    "parse-json": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+      "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+      "dev": true,
+      "requires": {
+        "error-ex": "^1.3.1",
+        "json-parse-better-errors": "^1.0.1"
+      }
+    },
+    "path-exists": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+      "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+      "dev": true,
+      "requires": {
+        "pinkie-promise": "^2.0.0"
+      }
+    },
+    "path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+      "dev": true
+    },
+    "path-key": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+      "dev": true
+    },
+    "path-parse": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+      "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+      "dev": true
+    },
+    "path-type": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+      "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
+      "dev": true,
+      "requires": {
+        "pify": "^3.0.0"
+      }
+    },
+    "performance-now": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
+      "dev": true
+    },
+    "picomatch": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz",
+      "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==",
+      "dev": true
+    },
+    "pify": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+      "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+      "dev": true
+    },
+    "pinkie": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+      "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
+      "dev": true
+    },
+    "pinkie-promise": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+      "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+      "dev": true,
+      "requires": {
+        "pinkie": "^2.0.0"
+      }
+    },
+    "process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+      "dev": true
+    },
+    "protobufjs": {
+      "version": "6.9.0",
+      "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.9.0.tgz",
+      "integrity": "sha512-LlGVfEWDXoI/STstRDdZZKb/qusoAWUnmLg9R8OLSO473mBLWHowx8clbX5/+mKDEI+v7GzjoK9tRPZMMcoTrg==",
+      "dev": true,
+      "requires": {
+        "@protobufjs/aspromise": "^1.1.2",
+        "@protobufjs/base64": "^1.1.2",
+        "@protobufjs/codegen": "^2.0.4",
+        "@protobufjs/eventemitter": "^1.1.0",
+        "@protobufjs/fetch": "^1.1.0",
+        "@protobufjs/float": "^1.0.2",
+        "@protobufjs/inquire": "^1.1.0",
+        "@protobufjs/path": "^1.1.2",
+        "@protobufjs/pool": "^1.1.0",
+        "@protobufjs/utf8": "^1.1.0",
+        "@types/long": "^4.0.1",
+        "@types/node": "^13.7.0",
+        "long": "^4.0.0"
+      }
+    },
+    "pseudomap": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
+      "dev": true
+    },
+    "psl": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz",
+      "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==",
+      "dev": true
+    },
+    "punycode": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+      "dev": true
+    },
+    "read-pkg": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
+      "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
+      "dev": true,
+      "requires": {
+        "load-json-file": "^4.0.0",
+        "normalize-package-data": "^2.3.2",
+        "path-type": "^3.0.0"
+      }
+    },
+    "read-pkg-up": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
+      "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
+      "dev": true,
+      "requires": {
+        "find-up": "^1.0.0",
+        "read-pkg": "^1.0.0"
+      },
+      "dependencies": {
+        "load-json-file": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
+          "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "^4.1.2",
+            "parse-json": "^2.2.0",
+            "pify": "^2.0.0",
+            "pinkie-promise": "^2.0.0",
+            "strip-bom": "^2.0.0"
+          }
+        },
+        "parse-json": {
+          "version": "2.2.0",
+          "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+          "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+          "dev": true,
+          "requires": {
+            "error-ex": "^1.2.0"
+          }
+        },
+        "path-type": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
+          "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "^4.1.2",
+            "pify": "^2.0.0",
+            "pinkie-promise": "^2.0.0"
+          }
+        },
+        "pify": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+          "dev": true
+        },
+        "read-pkg": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
+          "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
+          "dev": true,
+          "requires": {
+            "load-json-file": "^1.0.0",
+            "normalize-package-data": "^2.3.2",
+            "path-type": "^1.0.0"
+          }
+        },
+        "strip-bom": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
+          "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
+          "dev": true,
+          "requires": {
+            "is-utf8": "^0.2.0"
+          }
+        }
+      }
+    },
+    "readable-stream": {
+      "version": "2.3.7",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+      "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+      "dev": true,
+      "requires": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
+      }
+    },
+    "readdirp": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz",
+      "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==",
+      "dev": true,
+      "requires": {
+        "picomatch": "^2.0.7"
+      }
+    },
+    "redent": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
+      "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
+      "dev": true,
+      "requires": {
+        "indent-string": "^2.1.0",
+        "strip-indent": "^1.0.1"
+      }
+    },
+    "relateurl": {
+      "version": "0.2.7",
+      "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
+      "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk="
+    },
+    "repeating": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
+      "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
+      "dev": true,
+      "requires": {
+        "is-finite": "^1.0.0"
+      }
+    },
+    "request": {
+      "version": "2.88.0",
+      "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
+      "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
+      "dev": true,
+      "requires": {
+        "aws-sign2": "~0.7.0",
+        "aws4": "^1.8.0",
+        "caseless": "~0.12.0",
+        "combined-stream": "~1.0.6",
+        "extend": "~3.0.2",
+        "forever-agent": "~0.6.1",
+        "form-data": "~2.3.2",
+        "har-validator": "~5.1.0",
+        "http-signature": "~1.2.0",
+        "is-typedarray": "~1.0.0",
+        "isstream": "~0.1.2",
+        "json-stringify-safe": "~5.0.1",
+        "mime-types": "~2.1.19",
+        "oauth-sign": "~0.9.0",
+        "performance-now": "^2.1.0",
+        "qs": "~6.5.2",
+        "safe-buffer": "^5.1.2",
+        "tough-cookie": "~2.4.3",
+        "tunnel-agent": "^0.6.0",
+        "uuid": "^3.3.2"
+      },
+      "dependencies": {
+        "qs": {
+          "version": "6.5.2",
+          "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+          "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+          "dev": true
+        }
+      }
+    },
+    "require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+      "dev": true
+    },
+    "require-main-filename": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
+      "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
+      "dev": true
+    },
+    "resolve": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.14.1.tgz",
+      "integrity": "sha512-fn5Wobh4cxbLzuHaE+nphztHy43/b++4M6SsGFC2gB8uYwf0C8LcarfCz1un7UTW8OFQg9iNjZ4xpcFVGebDPg==",
+      "dev": true,
+      "requires": {
+        "path-parse": "^1.0.6"
+      }
+    },
+    "rimraf": {
+      "version": "2.7.1",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+      "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+      "dev": true,
+      "requires": {
+        "glob": "^7.1.3"
+      }
+    },
+    "rw": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
+      "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q="
+    },
+    "rx": {
+      "version": "2.3.24",
+      "resolved": "https://registry.npmjs.org/rx/-/rx-2.3.24.tgz",
+      "integrity": "sha1-FPlQpCF9fjXapxu8vljv9o6ksrc=",
+      "dev": true
+    },
+    "safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+      "dev": true
+    },
+    "safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+    },
+    "sass-graph": {
+      "version": "2.2.4",
+      "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz",
+      "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=",
+      "dev": true,
+      "requires": {
+        "glob": "^7.0.0",
+        "lodash": "^4.0.0",
+        "scss-tokenizer": "^0.2.3",
+        "yargs": "^7.0.0"
+      },
+      "dependencies": {
+        "camelcase": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
+          "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
+          "dev": true
+        },
+        "yargs": {
+          "version": "7.1.0",
+          "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz",
+          "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=",
+          "dev": true,
+          "requires": {
+            "camelcase": "^3.0.0",
+            "cliui": "^3.2.0",
+            "decamelize": "^1.1.1",
+            "get-caller-file": "^1.0.1",
+            "os-locale": "^1.4.0",
+            "read-pkg-up": "^1.0.1",
+            "require-directory": "^2.1.1",
+            "require-main-filename": "^1.0.1",
+            "set-blocking": "^2.0.0",
+            "string-width": "^1.0.2",
+            "which-module": "^1.0.0",
+            "y18n": "^3.2.1",
+            "yargs-parser": "^5.0.0"
+          }
+        },
+        "yargs-parser": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz",
+          "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=",
+          "dev": true,
+          "requires": {
+            "camelcase": "^3.0.0"
+          }
+        }
+      }
+    },
+    "scope-css": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/scope-css/-/scope-css-1.2.1.tgz",
+      "integrity": "sha512-UjLRmyEYaDNiOS673xlVkZFlVCtckJR/dKgr434VMm7Lb+AOOqXKdAcY7PpGlJYErjXXJzKN7HWo4uRPiZZG0Q==",
+      "requires": {
+        "escaper": "^2.5.3",
+        "slugify": "^1.3.1",
+        "strip-css-comments": "^3.0.0"
+      }
+    },
+    "scss-tokenizer": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz",
+      "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=",
+      "dev": true,
+      "requires": {
+        "js-base64": "^2.1.8",
+        "source-map": "^0.4.2"
+      }
+    },
+    "semver": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+      "dev": true
+    },
+    "set-blocking": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+      "dev": true
+    },
+    "shebang-command": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+      "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+      "dev": true,
+      "requires": {
+        "shebang-regex": "^1.0.0"
+      }
+    },
+    "shebang-regex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+      "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+      "dev": true
+    },
+    "signal-exit": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+      "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+      "dev": true
+    },
+    "slugify": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.4.0.tgz",
+      "integrity": "sha512-FtLNsMGBSRB/0JOE2A0fxlqjI6fJsgHGS13iTuVT28kViI4JjUiNqp/vyis0ZXYcMnpR3fzGNkv+6vRlI2GwdQ=="
+    },
+    "source-map": {
+      "version": "0.4.4",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
+      "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
+      "dev": true,
+      "requires": {
+        "amdefine": ">=0.0.4"
+      }
+    },
+    "source-map-support": {
+      "version": "0.5.19",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
+      "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
+      "requires": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+        }
+      }
+    },
+    "spawn-command": {
+      "version": "0.0.2-1",
+      "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
+      "integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=",
+      "dev": true
+    },
+    "spdx-correct": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
+      "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
+      "dev": true,
+      "requires": {
+        "spdx-expression-parse": "^3.0.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "spdx-exceptions": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
+      "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==",
+      "dev": true
+    },
+    "spdx-expression-parse": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
+      "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
+      "dev": true,
+      "requires": {
+        "spdx-exceptions": "^2.1.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "spdx-license-ids": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
+      "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==",
+      "dev": true
+    },
+    "sshpk": {
+      "version": "1.16.1",
+      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
+      "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
+      "dev": true,
+      "requires": {
+        "asn1": "~0.2.3",
+        "assert-plus": "^1.0.0",
+        "bcrypt-pbkdf": "^1.0.0",
+        "dashdash": "^1.12.0",
+        "ecc-jsbn": "~0.1.1",
+        "getpass": "^0.1.1",
+        "jsbn": "~0.1.0",
+        "safer-buffer": "^2.0.2",
+        "tweetnacl": "~0.14.0"
+      }
+    },
+    "stdout-stream": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz",
+      "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==",
+      "dev": true,
+      "requires": {
+        "readable-stream": "^2.0.1"
+      }
+    },
+    "string-width": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+      "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+      "dev": true,
+      "requires": {
+        "code-point-at": "^1.0.0",
+        "is-fullwidth-code-point": "^1.0.0",
+        "strip-ansi": "^3.0.0"
+      }
+    },
+    "string_decoder": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+      "dev": true,
+      "requires": {
+        "safe-buffer": "~5.1.0"
+      }
+    },
+    "strip-ansi": {
+      "version": "3.0.1",
+      "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+      "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+      "dev": true,
+      "requires": {
+        "ansi-regex": "^2.0.0"
+      }
+    },
+    "strip-bom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+      "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+      "dev": true
+    },
+    "strip-css-comments": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-css-comments/-/strip-css-comments-3.0.0.tgz",
+      "integrity": "sha1-elYl7/iisibPiUehElTaluE9rok=",
+      "requires": {
+        "is-regexp": "^1.0.0"
+      }
+    },
+    "strip-indent": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
+      "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
+      "dev": true,
+      "requires": {
+        "get-stdin": "^4.0.1"
+      }
+    },
+    "supports-color": {
+      "version": "3.2.3",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
+      "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
+      "dev": true,
+      "requires": {
+        "has-flag": "^1.0.0"
+      },
+      "dependencies": {
+        "has-flag": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
+          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
+          "dev": true
+        }
+      }
+    },
+    "tar": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz",
+      "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==",
+      "dev": true,
+      "requires": {
+        "block-stream": "*",
+        "fstream": "^1.0.12",
+        "inherits": "2"
+      }
+    },
+    "terser": {
+      "version": "4.6.13",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.13.tgz",
+      "integrity": "sha512-wMvqukYgVpQlymbnNbabVZbtM6PN63AzqexpwJL8tbh/mRT9LE5o+ruVduAGL7D6Fpjl+Q+06U5I9Ul82odAhw==",
+      "requires": {
+        "commander": "^2.20.0",
+        "source-map": "~0.6.1",
+        "source-map-support": "~0.5.12"
+      },
+      "dependencies": {
+        "commander": {
+          "version": "2.20.3",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+          "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+        }
+      }
+    },
+    "to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dev": true,
+      "requires": {
+        "is-number": "^7.0.0"
+      }
+    },
+    "tough-cookie": {
+      "version": "2.4.3",
+      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
+      "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
+      "dev": true,
+      "requires": {
+        "psl": "^1.1.24",
+        "punycode": "^1.4.1"
+      },
+      "dependencies": {
+        "punycode": {
+          "version": "1.4.1",
+          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+          "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+          "dev": true
+        }
+      }
+    },
+    "tree-kill": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+      "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
+      "dev": true
+    },
+    "trim-newlines": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
+      "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=",
+      "dev": true
+    },
+    "true-case-path": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz",
+      "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==",
+      "dev": true,
+      "requires": {
+        "glob": "^7.1.2"
+      }
+    },
+    "try-catch": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/try-catch/-/try-catch-2.0.1.tgz",
+      "integrity": "sha512-LsOrmObN/2WdM+y2xG+t16vhYrQsnV8wftXIcIOWZhQcBJvKGYuamJGwnU98A7Jxs2oZNkJztXlphEOoA0DWqg=="
+    },
+    "try-to-catch": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/try-to-catch/-/try-to-catch-1.1.1.tgz",
+      "integrity": "sha512-ikUlS+/BcImLhNYyIgZcEmq4byc31QpC+46/6Jm5ECWkVFhf8SM2Fp/0pMVXPX6vk45SMCwrP4Taxucne8I0VA=="
+    },
+    "tunnel-agent": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+      "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+      "dev": true,
+      "requires": {
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "tweetnacl": {
+      "version": "0.14.5",
+      "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+      "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+      "dev": true
+    },
+    "type-fest": {
+      "version": "0.8.1",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+      "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="
+    },
+    "uglify-js": {
+      "version": "3.9.2",
+      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.9.2.tgz",
+      "integrity": "sha512-zGVwKslUAD/EeqOrD1nQaBmXIHl1Vw371we8cvS8I6mYK9rmgX5tv8AAeJdfsQ3Kk5mGax2SVV/AizxdNGhl7Q==",
+      "requires": {
+        "commander": "~2.20.3"
+      },
+      "dependencies": {
+        "commander": {
+          "version": "2.20.3",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+          "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+        }
+      }
+    },
+    "universalify": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz",
+      "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==",
+      "dev": true
+    },
+    "upper-case": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
+      "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg="
+    },
+    "uri-js": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
+      "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+      "dev": true,
+      "requires": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+      "dev": true
+    },
+    "uuid": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+      "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+      "dev": true
+    },
+    "validate-npm-package-license": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+      "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+      "dev": true,
+      "requires": {
+        "spdx-correct": "^3.0.0",
+        "spdx-expression-parse": "^3.0.0"
+      }
+    },
+    "verror": {
+      "version": "1.10.0",
+      "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+      "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+      "dev": true,
+      "requires": {
+        "assert-plus": "^1.0.0",
+        "core-util-is": "1.0.2",
+        "extsprintf": "^1.2.0"
+      }
+    },
+    "which": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+      "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+      "dev": true,
+      "requires": {
+        "isexe": "^2.0.0"
+      }
+    },
+    "which-module": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
+      "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=",
+      "dev": true
+    },
+    "wide-align": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+      "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+      "dev": true,
+      "requires": {
+        "string-width": "^1.0.2 || 2"
+      }
+    },
+    "wrap-ansi": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+      "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
+      "dev": true,
+      "requires": {
+        "string-width": "^1.0.1",
+        "strip-ansi": "^3.0.1"
+      }
+    },
+    "wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+      "dev": true
+    },
+    "y18n": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
+      "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
+      "dev": true
+    },
+    "yallist": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+      "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+      "dev": true
+    },
+    "yargs": {
+      "version": "15.1.0",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.1.0.tgz",
+      "integrity": "sha512-T39FNN1b6hCW4SOIk1XyTOWxtXdcen0t+XYrysQmChzSipvhBO8Bj0nK1ozAasdk24dNWuMZvr4k24nz+8HHLg==",
+      "dev": true,
+      "requires": {
+        "cliui": "^6.0.0",
+        "decamelize": "^1.2.0",
+        "find-up": "^4.1.0",
+        "get-caller-file": "^2.0.1",
+        "require-directory": "^2.1.1",
+        "require-main-filename": "^2.0.0",
+        "set-blocking": "^2.0.0",
+        "string-width": "^4.2.0",
+        "which-module": "^2.0.0",
+        "y18n": "^4.0.0",
+        "yargs-parser": "^16.1.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+          "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+          "dev": true
+        },
+        "ansi-styles": {
+          "version": "4.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
+          "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+          "dev": true,
+          "requires": {
+            "@types/color-name": "^1.1.1",
+            "color-convert": "^2.0.1"
+          }
+        },
+        "cliui": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
+          "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+          "dev": true,
+          "requires": {
+            "string-width": "^4.2.0",
+            "strip-ansi": "^6.0.0",
+            "wrap-ansi": "^6.2.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "find-up": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+          "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+          "dev": true,
+          "requires": {
+            "locate-path": "^5.0.0",
+            "path-exists": "^4.0.0"
+          }
+        },
+        "get-caller-file": {
+          "version": "2.0.5",
+          "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+          "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+          "dev": true
+        },
+        "is-fullwidth-code-point": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+          "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+          "dev": true
+        },
+        "path-exists": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+          "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+          "dev": true
+        },
+        "require-main-filename": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+          "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+          "dev": true
+        },
+        "string-width": {
+          "version": "4.2.0",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
+          "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+          "dev": true,
+          "requires": {
+            "emoji-regex": "^8.0.0",
+            "is-fullwidth-code-point": "^3.0.0",
+            "strip-ansi": "^6.0.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+          "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^5.0.0"
+          }
+        },
+        "which-module": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+          "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
+          "dev": true
+        },
+        "wrap-ansi": {
+          "version": "6.2.0",
+          "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+          "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.0.0",
+            "string-width": "^4.1.0",
+            "strip-ansi": "^6.0.0"
+          }
+        },
+        "y18n": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
+          "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
+          "dev": true
+        }
+      }
+    },
+    "yargs-parser": {
+      "version": "16.1.0",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-16.1.0.tgz",
+      "integrity": "sha512-H/V41UNZQPkUMIT5h5hiwg4QKIY1RPvoBV4XcjUbRM8Bk2oKqqyZ0DIEbTFZB0XjbtSPG8SAa/0DxCQmiRgzKg==",
+      "dev": true,
+      "requires": {
+        "camelcase": "^5.0.0",
+        "decamelize": "^1.2.0"
+      },
+      "dependencies": {
+        "camelcase": {
+          "version": "5.3.1",
+          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+          "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+          "dev": true
+        }
+      }
+    }
+  }
+}
diff --git a/infra/perfetto.dev/package.json b/infra/perfetto.dev/package.json
new file mode 100644
index 0000000..791be22
--- /dev/null
+++ b/infra/perfetto.dev/package.json
@@ -0,0 +1,31 @@
+{
+  "name": "perfetto-dev",
+  "version": "1.0.0",
+  "description": "Perfetto Site",
+  "repository": "https://perfetto.dev",
+  "main": "main.js",
+  "author": "Perfetto Team",
+  "license": "Apache-2.0",
+  "dependencies": {
+    "mermaid": "^8.5.0"
+  },
+  "devDependencies": {
+    "concurrently": "^3.5.0",
+    "ejs": "^3.0.1",
+    "fs-extra": "^9.0.0",
+    "highlight.js": "^9.18.1",
+    "marked": "^0.8.0",
+    "node-sass": "^4.13.1",
+    "onchange": "^6.1.0",
+    "protobufjs": "^6.9.0",
+    "yargs": "^15.1.0"
+  },
+  "scripts": {
+    "clean": "../../tools/gn clean ../../out/perfetto.dev",
+    "setup": "ln -fns ../../out/perfetto.dev/site dist",
+    "serve": "npm run setup; dev_appserver.py .",
+    "build": "npm run setup; ../../tools/gn gen --args='' ../../out/perfetto.dev; ../../tools/ninja -C ../../out/perfetto.dev infra/perfetto.dev:node_modules; ../../tools/ninja -C ../../out/perfetto.dev site",
+    "watch": "onchange -v '../../docs/**' '**' '../../src/trace_processor/tables/**' '../../src/trace_processor/storage/stats.h' '../../protos/**' -e 'dist' -e 'dist/**' -- npm run build",
+    "start": "npm run build; concurrently \"npm run serve\" \"npm run watch\""
+  }
+}
diff --git a/infra/perfetto.dev/run-dev-server b/infra/perfetto.dev/run-dev-server
new file mode 100755
index 0000000..0915cc4
--- /dev/null
+++ b/infra/perfetto.dev/run-dev-server
@@ -0,0 +1,24 @@
+#!/bin/bash
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e
+
+readonly CUR_DIR="$(cd -P ${BASH_SOURCE[0]%/*}; pwd)"
+readonly ROOT_DIR=$(dirname $(dirname "$CUR_DIR"))
+readonly NODE_DIR="$ROOT_DIR/buildtools/nodejs/bin"
+readonly WRAPPER="$ROOT_DIR/gn/standalone/build_tool_wrapper.py"
+
+cd "$CUR_DIR"
+exec python "$WRAPPER" --path $NODE_DIR npm run start
diff --git a/infra/perfetto.dev/src/assets/README.md b/infra/perfetto.dev/src/assets/README.md
new file mode 100644
index 0000000..9f6190a
--- /dev/null
+++ b/infra/perfetto.dev/src/assets/README.md
@@ -0,0 +1,2 @@
+The vector drawings (.sketch) are stored in the internal teams drive
+under Perfetto -> Graphics -> "Perfetto Site.sketch"
diff --git a/infra/perfetto.dev/src/assets/analysis.png b/infra/perfetto.dev/src/assets/analysis.png
new file mode 100644
index 0000000..f2ccdd6
--- /dev/null
+++ b/infra/perfetto.dev/src/assets/analysis.png
Binary files differ
diff --git a/infra/perfetto.dev/src/assets/app_tracing.png b/infra/perfetto.dev/src/assets/app_tracing.png
new file mode 100644
index 0000000..18fdc10
--- /dev/null
+++ b/infra/perfetto.dev/src/assets/app_tracing.png
Binary files differ
diff --git a/infra/perfetto.dev/src/assets/brand.png b/infra/perfetto.dev/src/assets/brand.png
new file mode 100644
index 0000000..29afdc4
--- /dev/null
+++ b/infra/perfetto.dev/src/assets/brand.png
Binary files differ
diff --git a/infra/perfetto.dev/src/assets/favicon.png b/infra/perfetto.dev/src/assets/favicon.png
new file mode 100644
index 0000000..c405e24
--- /dev/null
+++ b/infra/perfetto.dev/src/assets/favicon.png
Binary files differ
diff --git a/infra/perfetto.dev/src/assets/home.png b/infra/perfetto.dev/src/assets/home.png
new file mode 100644
index 0000000..935e2c3
--- /dev/null
+++ b/infra/perfetto.dev/src/assets/home.png
Binary files differ
diff --git a/infra/perfetto.dev/src/assets/script.js b/infra/perfetto.dev/src/assets/script.js
new file mode 100644
index 0000000..91c57e5
--- /dev/null
+++ b/infra/perfetto.dev/src/assets/script.js
@@ -0,0 +1,287 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+'use strict';
+
+let tocAnchors = [];
+let lastMouseOffY = 0;
+let onloadFired = false;
+const postLoadActions = [];
+let tocEventHandlersInstalled = false;
+let resizeObserver = undefined;
+
+// Handles redirects from the old docs.perfetto.dev.
+const legacyRedirectMap = {
+  '#/contributing': '/docs/contributing/getting-started#community',
+  '#/build-instructions': '/docs/contributing/build-instructions',
+  '#/testing': '/docs/contributing/testing',
+  '#/app-instrumentation': '/docs/instrumentation/tracing-sdk',
+  '#/recording-traces': '/docs/instrumentation/tracing-sdk#recording',
+  '#/running': '/docs/quickstart/android-tracing',
+  '#/long-traces': '/docs/concepts/config#long-traces',
+  '#/detached-mode': '/docs/concepts/detached-mode',
+  '#/heapprofd': '/docs/data-sources/native-heap-profiler',
+  '#/java-hprof': '/docs/data-sources/java-heap-profiler',
+  '#/trace-processor': '/docs/analysis/trace-processor',
+  '#/analysis': '/docs/analysis/trace-processor#annotations',
+  '#/metrics': '/docs/analysis/metrics',
+  '#/traceconv': '/docs/quickstart/traceconv',
+  '#/clock-sync': '/docs/concepts/clock-sync',
+  '#/architecture': '/docs/concepts/service-model',
+};
+
+function doAfterLoadEvent(action) {
+  if (onloadFired) {
+    return action();
+  }
+  postLoadActions.push(action);
+}
+
+function setupSandwichMenu() {
+  const header = document.querySelector('.site-header');
+  const docsNav = document.querySelector('.nav');
+  const menu = header.querySelector('.menu');
+  menu.addEventListener('click', (e) => {
+    e.preventDefault();
+
+    // If we are displaying any /docs, toggle the navbar instead (the TOC).
+    if (docsNav) {
+      // |after_first_click| is to avoid spurious transitions on page load.
+      docsNav.classList.add('after_first_click');
+      updateNav();
+      setTimeout(() => docsNav.classList.toggle('expanded'), 0);
+    } else {
+      header.classList.toggle('expanded');
+    }
+  });
+}
+
+// (Re-)Generates the Table Of Contents for docs (the right-hand-side one).
+function updateTOC() {
+  const tocContainer = document.querySelector('.docs .toc');
+  if (!tocContainer)
+    return;
+  const toc = document.createElement('ul');
+  const anchors = document.querySelectorAll('.doc a.anchor');
+  tocAnchors = [];
+  for (const anchor of anchors) {
+    const li = document.createElement('li');
+    const link = document.createElement('a');
+    link.innerText = anchor.parentElement.innerText;
+    link.href = anchor.href;
+    link.onclick = () => {
+      onScroll(link)
+    };
+    li.appendChild(link);
+    if (anchor.parentElement.tagName === 'H3')
+      li.style.paddingLeft = '10px';
+    toc.appendChild(li);
+    doAfterLoadEvent(() => {
+      tocAnchors.push(
+          {top: anchor.offsetTop + anchor.offsetHeight / 2, obj: link});
+    });
+  }
+  tocContainer.innerHTML = '';
+  tocContainer.appendChild(toc);
+
+  // Add event handlers on the first call (can be called more than once to
+  // recompute anchors on resize).
+  if (tocEventHandlersInstalled)
+    return;
+  tocEventHandlersInstalled = true;
+  const doc = document.querySelector('.doc');
+  const passive = {passive: true};
+  if (doc) {
+    const offY = doc.offsetTop;
+    doc.addEventListener('mousemove', (e) => onMouseMove(offY, e), passive);
+    doc.addEventListener('mouseleave', () => {
+      lastMouseOffY = 0;
+    }, passive);
+  }
+  window.addEventListener('scroll', () => onScroll(), passive);
+  resizeObserver = new ResizeObserver(() => requestAnimationFrame(() => {
+                                        updateNav();
+                                        updateTOC();
+                                      }));
+  resizeObserver.observe(doc);
+}
+
+// Highlights the current TOC anchor depending on the scroll offset.
+function onMouseMove(offY, e) {
+  lastMouseOffY = e.clientY - offY;
+  onScroll();
+}
+
+function onScroll(forceHighlight) {
+  const y = document.documentElement.scrollTop + lastMouseOffY;
+  let highEl = undefined;
+  for (const x of tocAnchors) {
+    if (y < x.top)
+      continue;
+    highEl = x.obj;
+  }
+  for (const link of document.querySelectorAll('.docs .toc a')) {
+    if ((!forceHighlight && link === highEl) || (forceHighlight === link)) {
+      link.classList.add('highlighted');
+    } else {
+      link.classList.remove('highlighted');
+    }
+  }
+}
+
+// This function needs to be idempotent as it is called more than once (on every
+// resize).
+function updateNav() {
+  const curDoc = document.querySelector('.doc');
+  let curFileName = '';
+  if (curDoc)
+    curFileName = curDoc.dataset['mdFile'];
+
+  // First identify all the top-level nav entries (Quickstart, Data Sources,
+  // ...) and make them compressible.
+  const toplevelSections = document.querySelectorAll('.docs .nav > ul > li');
+  const toplevelLinks = [];
+  for (const sec of toplevelSections) {
+    const childMenu = sec.querySelector('ul');
+    if (!childMenu) {
+      // Don't make it compressible if it has no children (e.g. the very
+      // first 'Introduction' link).
+      continue;
+    }
+
+    // Don't make it compressible if the entry has an actual link (e.g. the very
+    // first 'Introduction' link), because otherwise it become ambiguous whether
+    // the link should toggle or open the link.
+    const link = sec.querySelector('a');
+    if (!link || !link.href.endsWith('#'))
+      continue;
+
+    sec.classList.add('compressible');
+
+    // Remember the compressed status as long as the page is opened, so clicking
+    // through links keeps the sidebar in a consistent visual state.
+    const memoKey = `docs.nav.compressed[${link.innerHTML}]`;
+
+    if (sessionStorage.getItem(memoKey) === '1') {
+      sec.classList.add('compressed');
+    }
+    doAfterLoadEvent(() => {
+      childMenu.style.maxHeight = `${childMenu.scrollHeight + 40}px`;
+    });
+
+    toplevelLinks.push(link);
+    link.onclick = (evt) => {
+      evt.preventDefault();
+      sec.classList.toggle('compressed');
+      if (sec.classList.contains('compressed')) {
+        sessionStorage.setItem(memoKey, '1');
+      } else {
+        sessionStorage.removeItem(memoKey);
+      }
+    };
+  }
+
+  const exps = document.querySelectorAll('.docs .nav ul a');
+  let found = false;
+  for (const x of exps) {
+    // If the url of the entry matches the url of the page, mark the item as
+    // highlighted and expand all its parents.
+    if (!x.href)
+      continue;
+    const url = new URL(x.href);
+    if (x.href.endsWith('#')) {
+      // This is a non-leaf link to a menu.
+      if (toplevelLinks.indexOf(x) < 0) {
+        x.removeAttribute('href');
+      }
+    } else if (url.pathname === curFileName && !found) {
+      x.classList.add('selected');
+      doAfterLoadEvent(() => x.scrollIntoViewIfNeeded());
+      found = true;  // Highlight only the first occurrence.
+    }
+  }
+}
+
+// If the page contains a ```mermaid ``` block, lazily loads the plugin and
+// renders.
+function initMermaid() {
+  const graphs = document.querySelectorAll('.mermaid');
+
+  // Skip if there are no mermaid graphs to render.
+  if (!graphs.length)
+    return;
+
+  const script = document.createElement('script');
+  script.type = 'text/javascript';
+  script.src = '/assets/mermaid.min.js';
+  const themeCSS = `
+  .cluster rect { fill: #FCFCFC; stroke: #ddd }
+  .node rect { fill: #DCEDC8; stroke: #8BC34A}
+  .edgeLabel:not(:empty) {
+      border-radius: 6px;
+      font-size: 0.9em;
+      padding: 4px;
+      background: #F5F5F5;
+      border: 1px solid #DDDDDD;
+      color: #666;
+  }
+  `;
+  script.addEventListener('load', () => {
+    mermaid.initialize({
+      startOnLoad: false,
+      themeCSS: themeCSS,
+      securityLevel: 'loose',  // To allow #in-page-links
+    });
+    for (const graph of graphs) {
+      requestAnimationFrame(() => {
+        mermaid.init(undefined, graph);
+        graph.classList.add('rendered');
+      });
+    }
+  })
+  document.body.appendChild(script);
+}
+
+window.addEventListener('DOMContentLoaded', () => {
+  updateNav();
+  updateTOC();
+});
+
+window.addEventListener('load', () => {
+  setupSandwichMenu();
+  initMermaid();
+
+  // Don't smooth-scroll on pages that are too long (e.g. reference pages).
+  if (document.body.scrollHeight < 10000) {
+    document.documentElement.style.scrollBehavior = 'smooth';
+  } else {
+    document.documentElement.style.scrollBehavior = 'initial';
+  }
+
+  onloadFired = true;
+  while (postLoadActions.length > 0) {
+    postLoadActions.shift()();
+  }
+
+  updateTOC();
+
+  // Enable animations only after the load event. This is to prevent glitches
+  // when switching pages.
+  document.documentElement.style.setProperty('--anim-enabled', '1')
+});
+
+const fragment = location.hash.split('?')[0].replace('.md', '');
+if (fragment in legacyRedirectMap) {
+  location.replace(legacyRedirectMap[fragment]);
+}
\ No newline at end of file
diff --git a/infra/perfetto.dev/src/assets/sprite.png b/infra/perfetto.dev/src/assets/sprite.png
new file mode 100644
index 0000000..f076c5a
--- /dev/null
+++ b/infra/perfetto.dev/src/assets/sprite.png
Binary files differ
diff --git a/infra/perfetto.dev/src/assets/style.scss b/infra/perfetto.dev/src/assets/style.scss
new file mode 100644
index 0000000..5fc742e
--- /dev/null
+++ b/infra/perfetto.dev/src/assets/style.scss
@@ -0,0 +1,856 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// -----------------------------------------------------------------------------
+// Common + CSS reset
+// -----------------------------------------------------------------------------
+:root {
+    --site-header-height: 50px;
+    --home-highlights-height: 128px;
+    --content-max-width: 1100px;
+    --anim-ease: cubic-bezier(0.4, 0.0, 0.2, 1);
+
+    // This is set to 1 by JS after onload. This is to prevent flickering on
+    // page load on the nav bar and other entries while transitioning in their
+    // initial state.
+    --anim-enabled: 0;
+
+    --anim-time: calc(0.15s * var(--anim-enabled));
+}
+
+$wide: "(max-width: 1100px)";
+$mobile: "(max-width: 768px)";
+
+@mixin minimal-scrollbar {
+    &::-webkit-scrollbar {
+        width: 8px;
+        background-color: transparent;
+    }
+    &::-webkit-scrollbar-thumb {
+        background-color: #ccc;
+        border-radius: 8px;
+    }
+}
+
+@media (max-aspect-ratio: 1/1) {
+     :root {
+        --home-highlights-height: 256px;
+    }
+}
+
+* {
+    box-sizing: border-box;
+    -webkit-tap-highlight-color: none;
+}
+
+html {
+    font-family: Roboto, sans-serif;
+    -webkit-font-smoothing: antialiased;
+}
+
+html,
+body {
+    padding: 0;
+    margin: 0;
+}
+
+h1,
+h2,
+h3 {
+    font-family: inherit;
+    font-size: inherit;
+    font-weight: inherit;
+    padding: 0;
+    margin: 0;
+}
+
+// -----------------------------------------------------------------------------
+// Site header
+// -----------------------------------------------------------------------------
+.site-header {
+    background-color: hsl(210, 30%, 16%);
+    color: hsl(210, 17%, 98%);
+    position: sticky; // Sticky so the .docs element below doesn't start @ 0.
+    top: 0;
+    width: 100%;
+    --sh-padding-y: 5px;
+    max-height: var(--site-header-height);
+    padding: var(--sh-padding-y) 30px;
+    box-shadow: rgba(0, 0, 0, 0.3) 0px 3px 3px 0px;
+    overflow: hidden;
+    display: flex;
+    z-index: 10;
+    transition: max-height var(--anim-ease) var(--anim-time);
+    &.expanded {
+        max-height: 100vh;
+    }
+    .brand {
+        img {
+            height: 40px;
+            vertical-align: bottom;
+        }
+        font-weight: 200;
+        font-size: 28px;
+        flex-grow: 1;
+        .brand-docs {
+            text-transform: uppercase;
+            font-size: 14px;
+            color: #ecba2a;
+            vertical-align: bottom;
+            line-height: 30px;
+            font-weight: 400;
+        }
+    }
+    >*:not(:first-child) {
+        line-height: calc(var(--site-header-height) - var(--sh-padding-y) * 2);
+        font-family: 'Source Sans Pro', sans-serif;
+        font-weight: 400;
+        font-size: 1.1rem;
+        margin: 0 20px;
+        color: hsl(210, 17%, 85%);
+    }
+    a {
+        text-decoration: none;
+        &:hover {
+            color: hsl(210, 17%, 100%);
+        }
+    }
+    .menu {
+        visibility: hidden;
+        font-family: 'Material Icons Round';
+        font-size: 24px;
+        text-align: center;
+        position: absolute;
+        right: 0;
+        top: 0;
+        line-height: var(--site-header-height);
+    }
+
+    @media #{$mobile} {
+        flex-direction: column;
+        >*:not(:first-child) {
+            margin-left: 40px;
+        }
+        .menu {
+            visibility: visible;
+        }
+    }
+}
+
+// -----------------------------------------------------------------------------
+// Site header
+// -----------------------------------------------------------------------------
+
+// Footer in the index page.
+.site-footer {
+    background-color: hsl(210, 30%, 16%);
+    padding: 1em 0;
+    font-size: 14px;
+    color: #fff;
+    text-align: center;
+    ul {
+        list-style: none;
+        margin: 0;
+        padding: 0;
+        li {
+            display: inline;
+            padding: 0 10px;
+            &:not(:last-child) {
+                border-right: solid 1px #fff;
+            }
+        }
+    }
+    a,
+    a:visited {
+        text-decoration: none;
+        color: inherit;
+    }
+    .docs-footer-notice { display: none; }
+}
+
+// Footer overrides for the /docs/ page.
+.docs .site-footer {
+    grid-area: footer;
+    background: transparent;
+    color: #666;
+    text-align: left;
+    margin: 0 20px;
+    padding: 12px 0;
+
+    .docs-footer-notice {
+        padding: 0;
+        margin: 0;
+        display: block;
+    }
+
+    ul { display: none; }
+}
+
+// -----------------------------------------------------------------------------
+// Site content
+// -----------------------------------------------------------------------------
+.site-content {
+    .section-wrapper {
+        border-bottom: solid 1px #eee;
+        &:nth-child(2n+1) {
+          background-color: hsl(210, 17%, 98%);
+        }
+    }
+    section {
+        display: block;
+        position: relative;
+        padding: 0 20px;
+        margin: 0 auto;
+        max-width: calc(var(--content-max-width) + 2 * 20px);
+    }
+
+    .banner {
+        height: calc(100vh - var(--home-highlights-height) - var(--site-header-height));
+        min-height: 25vw;
+        display: grid;
+        grid-template-columns: 1fr;
+        grid-template-rows: max-content max-content 1fr;
+        h1,
+        h2 {
+            margin: auto;
+            font-family: 'Source Sans Pro', sans-serif;
+            text-align: center;
+            color: hsl(0, 0, 35%);
+            span {
+                white-space: nowrap;
+            }
+        }
+        h1 {
+            font-size: 2.5rem;
+            font-size: calc(min(max(3.5vw, 2rem), 4rem));
+            font-weight: 400;
+            padding-top: 40px;
+        }
+        h2 {
+            font-size: 1.25rem;
+            font-size: calc(min(max(2vw, 1.25rem), 3rem));
+            font-weight: 200;
+            padding-top: 10px;
+        }
+        .home-img {
+            max-height: 100%;
+            max-width: 100%;
+            margin: auto;
+            padding: 20px 0;
+        }
+    }
+    .home-highlights {
+        &:before {
+            border-top: 1px solid hsl(210, 17%, 90%);
+        }
+        height: var(--home-highlights-height);
+        display: grid;
+        grid-template-columns: repeat(4, 1fr);
+        grid-template-rows: 1fr;
+        background-color: #fff;
+        z-index: 2;
+        @media (max-aspect-ratio: 1/1) {
+            grid-template-columns: repeat(2, 1fr);
+        }
+        >a {
+            color: hsl(0, 0, 20%);
+            font-size: 22px;
+            font-weight: 400;
+            text-align: center;
+            padding: 20px 0;
+            font-family: 'Source Sans Pro', sans-serif;
+            text-decoration: none;
+            .icon {
+                background-image: url('/assets/sprite.png');
+                background-repeat: no-repeat;
+                width: 64px;
+                height: 64px;
+                margin: auto;
+                background-size: 256px 128px;
+                filter: grayscale(1);
+                transition: filter ease var(--anim-time);
+            }
+            &:nth-child(1) .icon {
+                background-position: 0 -64px;
+            }
+            &:nth-child(2) .icon {
+                background-position: -64px -64px;
+            }
+            &:nth-child(3) .icon {
+                background-position: -128px -64px;
+            }
+            &:nth-child(4) .icon {
+                background-position: -192px -64px;
+            }
+            &:hover {
+                background-color: hsl(210, 17%, 90%);
+                .icon {
+                    filter: grayscale(0);
+                }
+            }
+        }
+    }
+    .home-section {
+        min-height: calc(min(100vh - var(--site-header-height), 800px));
+        padding: 5% 20px;
+        display: grid;
+        grid-template-rows: 1fr;
+        grid-column-gap: 4vw;
+        >img {
+            grid-area: img;
+            max-width: 100%;
+            max-height: 100%;
+            margin: auto;
+            margin-top: 40px;
+        }
+        h2,
+        >div {
+            grid-area: content;
+        }
+        h2 {
+            font-family: 'Source Sans Pro', sans-serif;
+            font-weight: 600;
+            font-size: 2.5rem;
+            color: #333;
+            text-align: center;
+        }
+        &:nth-child(2n) {
+            grid-template-columns: 5fr 4fr;
+            grid-template-areas: "content img";
+            h2 {
+                padding: 0 0 0 50px;
+                text-align: left;
+            }
+        }
+        &:nth-child(2n+1) {
+            grid-template-columns: 4fr 5fr;
+            grid-template-areas: "img content";
+            h2 {
+                padding: 0 50px 0 0;
+                text-align: left;
+            }
+        }
+        @media (max-aspect-ratio: 1/1) {
+            padding: 5vh 20px;
+            &:nth-child(n) {
+                grid-template-rows: auto auto;
+                grid-template-columns: 1fr;
+                grid-template-areas: "img" "content";
+                grid-row-gap: 30px;
+                h2 {
+                    padding: 0;
+                    text-align: center;
+                }
+            }
+            >img {
+                padding: 0 10vw;
+            }
+        }
+        div {
+            grid-area: content;
+            .button {
+                display: inline-block;
+                background: #337ab7;
+                font-weight: 500;
+                color: #fff;
+                border-radius: 6px;
+                font-size: 18px;
+                padding: 10px 16px;
+                transition: background-color ease var(--anim-time);
+                text-decoration: none;
+                &:hover {
+                    background: #286090;
+                }
+            }
+        }
+        .home-item {
+            display: grid;
+            grid-template-rows: auto auto;
+            grid-template-columns: 50px auto;
+            grid-template-areas: "img title" "img label";
+            grid-column-gap: 20px;
+            padding: 20px 30px;
+            margin: 10px 0;
+            // border: 1px solid #eee;
+            font-family: 'Source Sans Pro', sans-serif;
+            color: #111111;
+            transition: filter var(--anim-ease) var(--anim-time), background-color var(--anim-ease) var(--anim-time), transform var(--anim-ease) var(--anim-time), box-shadow linear var(--anim-time);
+            border-radius: 6px;
+            filter: opacity(0.6);
+            &:hover {
+                background-color: hsla(0, 0, 0, 0.02);
+                filter: opacity(1);
+                transform: scale(1.01);
+            }
+            >img,
+            >i {
+                grid-area: img;
+                margin: auto;
+                font-size: 50px;
+            }
+            >h3 {
+                grid-area: title;
+                font-size: 1.25rem;
+                line-height: 20px;
+                font-weight: 600;
+            }
+            >p {
+                grid-area: label;
+                font-size: 1rem;
+                font-weight: 400;
+                margin: 1em 0;
+            }
+        }
+    }
+}
+
+// -----------------------------------------------------------------------------
+// Docs
+// -----------------------------------------------------------------------------
+.docs {
+    min-height: 100vh;
+    display: grid;
+    --nav-width: 240px;
+    --toc-width: 180px;
+
+    // 1665px is the clientWidth on a macbook pro. Adjust the layout so that
+    // the max-width of the central .doc fits precisely when the browser is
+    // full-screen on a macbook.
+    --max-doc-width: calc(1665px - var(--toc-width) - var(--nav-width));
+
+    grid-template-columns: var(--nav-width) minmax(auto, var(--max-doc-width)) var(--toc-width);
+    grid-template-rows: 1fr max-content;
+    grid-template-areas: "nav doc toc" "nav footer toc";
+
+    background-color: hsl(210, 10%, 97%);
+    .nav {
+        grid-area: nav;
+        border-right: 1px solid hsl(210, 30%, 90%);
+        background-color: #fefefe;
+        padding: 20px 0;
+        padding-right: 16px;
+
+        position: sticky;
+        top: var(--site-header-height);
+        height: calc(100vh -  var(--site-header-height));
+        overflow-y: auto;
+        @include minimal-scrollbar;
+
+        a {
+            color: inherit;
+            text-decoration: none;
+            line-height: 24px;
+            display: flex;
+            transition: background-color var(--anim-ease) var(--anim-time),
+                        visibility linear var(--anim-time);
+            border-radius: 0 10px 10px 0;
+            -webkit-tap-highlight-color: transparent;
+            &[href] {
+                &:hover {
+                    color: #000;
+                    background-color: #f1f3f4;
+                }
+                &.selected {
+                    background-color: #ecba2a;
+                }
+            }
+        }
+
+        ul {
+            list-style: none;
+            margin: 0;
+            padding: 0;
+            overflow: hidden;
+            li {
+                font-size: 1rem;
+                font-weight: 400;
+                font-family: 'Source Sans Pro', sans-serif;
+                color: #4a4a4a;
+                max-width: 100%;
+                margin: 3px 0;
+            }
+            p { margin: 0; }
+        }
+
+        // Applies only to outer-level submenus.
+        >ul {
+            position: static; // Otherwise gets v-centered in the middle.
+            > li {
+                padding-bottom: 10px;
+                margin-bottom: 10px;
+                font-weight: 600;
+                color: #111;
+
+                &:not(:last-child) {
+                    border-bottom: 1px solid #eee;
+                }
+
+                &.compressible {
+                    > p > a::after {
+                        content: 'keyboard_arrow_up';
+                        font-family: 'Material Icons Round';
+                        font-size: 24px;
+                        width: 24px;
+                        transition: transform var(--anim-ease) var(--anim-time);
+                        margin: 0 0 0 auto;
+                        font-weight: 200;
+                        color: #666;
+                    }
+                    > ul {
+                        transition: max-height var(--anim-ease) var(--anim-time),
+                                    opacity var(--anim-ease) var(--anim-time);
+                        opacity: 1;
+                    }
+                    &.compressed {
+                        // The JS will compute and set the maxHeight on each
+                        // element depending on the size of their children.
+                        // !important is needed to override the element-inline
+                        // max-height property set by JS, which is prioritary.
+                        > ul {
+                            max-height: 0 !important;
+                            visibility: hidden;
+                            opacity: 0;
+                        }
+                        > p > a::after {
+                            transform: scaleY(-1);
+                        }
+                    }
+                }  // .compressible
+
+            }
+        }
+
+        li a {
+            padding-left: 16px;
+        }
+        li li a {
+            padding-left: 30px;
+        }
+        li li li a {
+            padding-left: 44px;
+        }
+        .expanded a::after {
+            transform: rotate(180deg);
+        }
+    }
+    .doc {
+        grid-area: doc;
+        background-color: #fff;
+        margin: 20px;
+        padding: 30px 40px;
+        font-family: Roboto, sans-serif;
+        font-size: 1rem;
+        font-weight: 400;
+        line-height: 24px;
+        -webkit-font-smoothing: antialiased;
+        color: #4a4a4a;
+        position: relative;
+        box-shadow: 0 1px 2px 0 rgba(60, 64, 67, .1), 0 1px 3px 1px rgba(60, 64, 67, .15);
+        overflow: hidden;
+
+        a {
+            text-decoration: none;
+            &:link { color: #007b83; }
+            &:visited { color: #8e3317; }
+            &:hover { color: #009da8; }
+            &[href^="http"] {
+                // External link.
+                &:after {
+                    content: 'open_in_new';
+                    font-family: 'Material Icons Round';
+                    color: #666;
+                    text-decoration: none;
+                    margin-left: 2px;
+                    margin-right: 4px;
+                    vertical-align: bottom;
+                }
+            }
+        }
+
+        h1,
+        h2,
+        h3 {
+            margin: 10px 0;
+            padding: 0;
+            padding-top: 30px;
+        }
+        h1 {
+            font-size: 2.25rem;
+            line-height: 2.25rem;
+            margin: 0;
+            padding: 0;
+            margin-bottom: 1.5rem;
+            font-family: 'Source Sans Pro', sans-serif;
+        }
+        h2 {
+            font-size: 1.5rem;
+            border-bottom: 1px solid #e8eaed;
+            padding-bottom: 6px;
+        }
+        h3 {
+            font-size: 1.25rem;
+        }
+        * {
+            max-width: 100%;
+        }
+
+        img[alt$="screenshot"] {
+            box-shadow: 0 0 10px 2px #eee;
+        }
+
+        code:not(.code-block) {
+            background: hsla(210, 17%, 90%, 0.2);
+            border: 1px solid #E8EAED;
+            border-radius: 6px;
+            padding: 1px 4px;
+        }
+        .code-block {
+            overflow-x: auto;
+            white-space: pre;
+            border-radius: 6px;
+            box-shadow: 1px 1px 6px #999;
+            border-top: 5px solid #8BC34A;
+        }
+        // Hide mermaid graphs until they are rendered, this is to avoid showing
+        // the mermaid source while the renderer generates the SVG.
+        .mermaid {
+            transition: opacity var(--anim-ease) var(--anim-time);
+            &:not(.rendered) {
+                opacity: 0;
+            }
+        }
+        .anchor {
+            margin-left: -29px;
+            padding-right: 5px;
+            text-decoration: none;
+            position: absolute;
+            padding-top: var(--site-header-height);
+            margin-top: calc(-1 * var(--site-header-height));
+            outline: none;
+            opacity: 0;
+            transition: opacity var(--anim-ease) var(--anim-time);
+            &::before {
+                content: 'insert_link';
+                font-family: 'Material Icons Round';
+                color: #333;
+                font-size: 24px;
+            }
+        }
+        *:hover .anchor {
+            opacity: 1;
+        }
+        code {
+            font-family: 'Roboto Mono', monospace;
+            font-size: 14px;
+        }
+        table {
+            width: 100%;
+            font-size: 14px;
+            border-spacing: 0;
+            border-collapse: collapse;
+            th, td {
+                padding: 8px;
+                border: 0 solid #dadce0;
+                border-top-width: 1px;
+                border-bottom-width: 1px;
+
+            }
+            tr {
+                height: 20px;
+            }
+            thead {
+                text-align: left;
+                background-color: #e8eaed;
+                color: #202124;
+            }
+        }
+
+        &[data-md-file^="/docs/reference/"] {
+            h1, h2, h3 {
+                code {
+                    margin-left: 20px;
+                    color: #666;
+                }
+            }
+            table {
+                width: 100%;
+                font-size: 14px;
+                border-spacing: 0;
+                border-collapse: collapse;
+                th, td {
+                    padding: 8px;
+                    border: 0 solid #dadce0;
+                    border-top-width: 1px;
+                    border-bottom-width: 1px;
+
+                }
+                tr {
+                    height: 20px;
+                }
+                thead {
+                    text-align: left;
+                    background-color: #e8eaed;
+                    color: #202124;
+                }
+                td {
+                    &:first-child { background: #f7f7f7; }
+
+                    /* Not really 100% but makes sure that the description
+                     * column takes most of the width */
+                    &:last-child { width: 80%; }
+                }
+            }
+        }
+
+        .callout {
+            padding: .5rem .5rem .5rem 2rem;
+            border: none;
+            border-radius: 2px;
+            margin-left: auto;
+            margin-right: auto;
+            width: 90%;
+            border-left: 3px solid transparent;
+            box-shadow: 0 0.2rem 0.5rem rgba(0,0,0,.05), 0 0 0.05rem rgba(0,0,0,.1);
+
+            &:before {
+                font-family: 'Material Icons Round';
+                position: absolute;
+                font-size: 1.5rem;
+                margin-left: -1.75rem;
+                margin-top: -2px;
+            }
+
+            &.note {
+                background-color: #E8F0FE;
+                border-color: #1967D2;
+                color: #1967D2;
+                &:before { content: 'bookmark'; }
+            }
+
+            &.summary {
+                background-color: #E4F7FB;
+                border-color:  #129EAF;
+                color: #129EAF;
+                &:before { content: 'sms'; }
+            }
+
+            &.tip {
+                background-color: #E6F4EA;
+                border-color: #188038;
+                color: #188038;
+                &:before { content: 'star'; }
+            }
+
+            &.todo {
+                background-color: #F1F3F4;
+                border-color: #5F6368;
+                color: #5F6368;
+                &:before { content: 'error'; }
+            }
+
+            &.warning {
+                background-color: #FCE8E6;
+                border-color: #C5221F;
+                color: #C5221F;
+                &:before { content: 'warning'; }
+            }
+        }
+    }
+    .toc {
+        grid-area: toc;
+        padding: 20px 16px 20px 0;
+
+        position: sticky;
+        top: var(--site-header-height);
+        height: calc(100vh - var(--site-header-height));
+        overflow-y: auto;
+        @include minimal-scrollbar;
+
+        font-family: 'Source Sans Pro', sans-serif;
+        word-break: break-word;
+        a {
+            text-decoration: none;
+        }
+        a,
+        a:visited {
+            color: #333;
+        }
+        a.highlighted {
+            font-weight: 500;
+            color: hsl(45, 100%, 40%);
+        }
+        font-size: 0.875rem;
+        ul {
+            list-style: none;
+            margin: 0;
+            padding: 0;
+            li {
+                margin: 5px 0;
+                /* This make it so that a single word gets elided but if there
+                 * are multiple words they span across lines.  */
+                overflow: hidden;
+                text-overflow: ellipsis;
+                white-space: break-spaces;
+                word-break: normal;
+            }
+        }
+        >ul {
+            border-left: 4px solid #ecba2a;
+            padding-left: 10px;
+            position: static;  // Otherwise gets v-centered in the middle.
+            top: calc(var(--site-header-height) + 25px);
+        }
+    }
+
+    @media #{$wide} {
+        grid-template-columns: var(--nav-width) auto 0;
+        .toc { display: none; }
+    }
+    @media #{$mobile} {
+        display: block;
+        .doc {
+            margin: 0;
+            padding: 20px;
+        }
+        .nav {
+            // JS will persistently toggle to .after_first_click. This is to
+            // avoid spurious transitions on page load.
+            display: none;
+
+            --nav-width-mobile: calc(min(90vw, 360px));
+            width: var(--nav-width-mobile);
+            position: fixed;
+            z-index: 2;
+            height: 100vh;
+            overflow-y: auto;
+            top: var(--site-header-height);
+            transition: transform var(--anim-ease) var(--anim-time),
+                        box-shadow var(--anim-ease) var(--anim-time),
+                        visibility ease var(--anim-time);
+            transform: translateX(calc(-1 * var(--nav-width-mobile)));
+            visibility: hidden;
+            >ul {
+                position: static;
+                top: 0;
+            }
+            &.after_first_click {
+                display: block;
+            }
+            &.expanded {
+                visibility: visible;
+                transform: translateX(0);
+                box-shadow: 0 1px 0 100vw rgba(0,0,0,0.4);
+            }
+        }
+    }
+}
diff --git a/infra/perfetto.dev/src/assets/sys_profiling.png b/infra/perfetto.dev/src/assets/sys_profiling.png
new file mode 100644
index 0000000..3135e16
--- /dev/null
+++ b/infra/perfetto.dev/src/assets/sys_profiling.png
Binary files differ
diff --git a/infra/perfetto.dev/src/assets/ui.png b/infra/perfetto.dev/src/assets/ui.png
new file mode 100644
index 0000000..331e0ea
--- /dev/null
+++ b/infra/perfetto.dev/src/assets/ui.png
Binary files differ
diff --git a/infra/perfetto.dev/src/gen_proto_reference.js b/infra/perfetto.dev/src/gen_proto_reference.js
new file mode 100644
index 0000000..044929e
--- /dev/null
+++ b/infra/perfetto.dev/src/gen_proto_reference.js
@@ -0,0 +1,147 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Generation of reference from protos
+
+'use strict';
+
+const protobufjs = require('protobufjs');
+const fs = require('fs');
+const path = require('path');
+const argv = require('yargs').argv
+
+const PROJECT_ROOT =
+    path.dirname(path.dirname(path.dirname(path.dirname(__filename))));
+
+const visited = {};
+
+
+// Removes \n due to 80col wrapping and preserves only end-of-sentence line
+// breaks.
+function singleLineComment(comment) {
+  comment = comment || '';
+  comment = comment.trim();
+  comment = comment.replace(/\.\n/g, '<br>');
+  comment = comment.replace(/\n/g, ' ');
+  return comment;
+}
+
+function getFullName(pType) {
+  let cur = pType;
+  let name = pType.name;
+  while (cur && cur.parent != cur && cur.parent instanceof protobufjs.Type) {
+    name = `${cur.parent.name}.${name}`;
+    cur = cur.parent;
+  }
+  return name;
+}
+
+function genType(pType, depth) {
+  depth = depth || 0;
+  console.assert(pType instanceof protobufjs.ReflectionObject);
+  const fullName = getFullName(pType);
+  if (fullName in visited)
+    return '';
+  visited[fullName] = true;
+
+  const heading = '#' +
+      '#'.repeat(Math.min(depth, 2));
+  const anchor = depth > 0 ? `{#${fullName}} ` : '';
+  let md = `${heading} ${anchor}${fullName}`;
+  md += '\n';
+  const fileName = path.basename(pType.filename);
+  const relPath = path.relative(PROJECT_ROOT, pType.filename);
+  md += `${(pType.comment || '').replace(/(\n)?^\s*next.*\bid:.*$/img, '')}`;
+  md += `\n\nDefined in [${fileName}](/${relPath})\n\n`;
+
+  const subTypes = [];
+
+  if (pType instanceof protobufjs.Enum) {
+    md += '#### Enum values:\n';
+    md += 'Name | Value | Description\n';
+    md += '---- | ----- | -----------\n';
+    for (const enumName of Object.keys(pType.values)) {
+      const enumVal = pType.values[enumName];
+      const comment = singleLineComment(pType.comments[enumName]);
+      md += `${enumName} | ${enumVal} | ${comment}\n`
+    }
+  } else {
+    md += '#### Fields:\n';
+    md += 'Field | Type | Description\n';
+    md += '----- | ---- | -----------\n';
+
+    for (const fieldName in pType.fields) {
+      const field = pType.fields[fieldName];
+      let type = field.type;
+      if (field.repeated) {
+        type = `${type}[]`;
+      }
+      if (field.resolvedType) {
+        // The TraceConfig proto is linked from the TracePacket reference.
+        // Instead of recursing and generating the TraceConfig types all over
+        // again, just link to the dedicated TraceConfig reference page.
+        if (getFullName(field.resolvedType) === 'TraceConfig') {
+          type = `[${type}](/docs/reference/trace-config-proto.autogen)`;
+        } else {
+          subTypes.push(field.resolvedType);
+          type = `[${type}](#${getFullName(field.resolvedType)})`;
+        }
+      }
+      md += `${fieldName} | ${type} | ${singleLineComment(field.comment)}\n`
+    }
+  }
+  md += '\n\n\n\n';
+
+  for (const subType of subTypes)
+    md += genType(subType, depth + 1);
+
+  return md;
+}
+
+
+function main() {
+  const inProtoFile = argv['i'];
+  const protoName = argv['p'];
+  const outFile = argv['o'];
+  if (!inProtoFile || !protoName) {
+    console.error('Usage: -i input.proto -p protos.RootType [-o out.md]');
+    process.exit(1);
+  }
+
+  const parser = new protobufjs.Root();
+  parser.resolvePath = (_, target) => {
+    if (target == inProtoFile) {
+      // The root proto file passed from the cmdline will be relative to the
+      // root_build_dir (out/xxx) (e.g.: ../../protos/config)
+      return inProtoFile;
+    }
+    // All the other imports, instead, will be relative to the project root
+    // (e.g. protos/config/...)
+    return path.join(PROJECT_ROOT, target);
+  };
+
+  const cfg = parser.loadSync(
+      inProtoFile, {alternateCommentMode: true, keepCase: true});
+  cfg.resolveAll();
+  const traceConfig = cfg.lookup(protoName);
+  const generatedMd = genType(traceConfig);
+  if (outFile) {
+    fs.writeFileSync(outFile, generatedMd);
+  } else {
+    console.log(generatedMd);
+  }
+  process.exit(0);
+}
+
+main();
diff --git a/infra/perfetto.dev/src/gen_sql_tables_reference.js b/infra/perfetto.dev/src/gen_sql_tables_reference.js
new file mode 100644
index 0000000..a605577
--- /dev/null
+++ b/infra/perfetto.dev/src/gen_sql_tables_reference.js
@@ -0,0 +1,305 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Generation of reference from protos
+
+'use strict';
+
+const fs = require('fs');
+const path = require('path');
+const argv = require('yargs').argv
+
+// Removes \n due to 80col wrapping and preserves only end-of-sentence line
+// breaks.
+// TODO dedupe, this is copied from the other gen_proto file.
+function singleLineComment(comment) {
+  comment = comment || '';
+  comment = comment.trim();
+  comment = comment.replace(/\.\n/g, '<br>');
+  comment = comment.replace(/\n/g, ' ');
+  return comment;
+}
+
+// Returns an object describing the table as follows:
+// { name: 'HeapGraphObjectTable',
+//   cols: [ {name: 'upid',            type: 'uint32_t', optional: false },
+//           {name: 'graph_sample_ts', type: 'int64_t',  optional: false },
+function parseTableDef(tableDefName, tableDef) {
+  const tableDesc = {
+    name: '',                // The SQL table name, e.g. stack_profile_mapping.
+    cppClassName: '',        // e.g., StackProfileMappingTable.
+    defMacro: tableDefName,  // e.g., PERFETTO_TP_STACK_PROFILE_MAPPING_DEF.
+    comment: '',
+    parent: undefined,   // Will be filled afterwards in the resolution phase.
+    parentDefName: '',   // e.g., PERFETTO_TP_STACK_PROFILE_MAPPING_DEF.
+    tablegroup: 'Misc',  // From @tablegroup in comments.
+    cols: {},
+  };
+  const getOrCreateColumn = (name) => {
+    if (name in tableDesc.cols)
+      return tableDesc.cols[name];
+    tableDesc.cols[name] = {
+      name: name,
+      type: '',
+      comment: '',
+      optional: false,
+      refTableCppName: undefined,
+      joinTable: undefined,
+      joinCol: undefined,
+    };
+    return tableDesc.cols[name];
+  };
+
+  let lastColumn = undefined;
+  for (const line of tableDef.split('\n')) {
+    if (line.startsWith('#define'))
+      continue;  // Skip the first line.
+    let m;
+    if (line.startsWith('//')) {
+      let comm = line.replace(/^\s*\/\/\s*/, '');
+      if (m = comm.match(/@tablegroup (.*)/)) {
+        tableDesc.tablegroup = m[1];
+        continue;
+      }
+      if (m = comm.match(/@name (\w+)/)) {
+        tableDesc.name = m[1];
+        continue;
+      }
+      if (m = comm.match(/@param\s+([^ ]+)\s*({\w+})?\s*(.*)/)) {
+        lastColumn = getOrCreateColumn(/*name=*/ m[1]);
+        lastColumn.type = (m[2] || '').replace(/(^{)|(}$)/g, '');
+        lastColumn.comment = m[3];
+        continue;
+      }
+      if (lastColumn === undefined) {
+        tableDesc.comment += `${comm}\n`;
+      } else {
+        lastColumn.comment = `${lastColumn.comment}${comm}\n`;
+      }
+      continue;
+    }
+    if (m = line.match(/^\s*NAME\((\w+)\s*,\s*"(\w+)"/)) {
+      tableDesc.cppClassName = m[1];
+      if (tableDesc.name === '') {
+        tableDesc.name = m[2];  // Set only if not overridden by @name.
+      }
+      continue;
+    }
+    if (m = line.match(/(PERFETTO_TP_ROOT_TABLE|PARENT)\((\w+)/)) {
+      if (m[1] === 'PARENT') {
+        tableDesc.parentDefName = m[2];
+      }
+      continue;
+    }
+    if (m = line.match(/^\s*C\(([^,]+)\s*,\s*(\w+)/)) {
+      const col = getOrCreateColumn(/*name=*/ m[2]);
+      col.type = m[1];
+      if (m = col.type.match(/Optional<(.*)>/)) {
+        col.type = m[1];
+        col.optional = true;
+      }
+      if (col.type === 'StringPool::Id') {
+        col.type = 'string';
+      }
+      const sep = col.type.indexOf('::');
+      if (sep > 0) {
+        col.refTableCppName = col.type.substr(0, sep);
+      }
+      continue;
+    }
+    throw new Error(`Cannot parse line "${line}" from ${tableDefName}`);
+  }
+
+  // Process {@joinable xxx} annotations.
+  const regex = /\s?\{@joinable\s*(\w+)\.(\w+)\s*\}/;
+  for (const col of Object.values(tableDesc.cols)) {
+    const m = col.comment.match(regex)
+    if (m) {
+      col.joinTable = m[1];
+      col.joinCol = m[2];
+      col.comment = col.comment.replace(regex, '');
+    }
+  }
+  return tableDesc;
+}
+
+
+function parseTablesInCppFile(filePath) {
+  const hdr = fs.readFileSync(filePath, 'UTF8');
+  const regex = /^\s*PERFETTO_TP_TABLE\((\w+)\)/mg;
+  let match = regex.exec(hdr);
+  const tables = [];
+  while (match != null) {
+    const tableDefName = match[1];
+    match = regex.exec(hdr);
+
+    // Now let's extract the table definition, that looks like this:
+    // // Some
+    // // Multiline
+    // // Comment
+    // #define PERFETTO_TP_STACK_PROFILE_FRAME_DEF(NAME, PARENT, C) \
+    // NAME(StackProfileFrameTable, "stack_profile_frame")        \
+    // PERFETTO_TP_ROOT_TABLE(PARENT, C)                          \
+    // C(StringPool::Id, name)                                    \
+    // C(StackProfileMappingTable::Id, mapping)                   \
+    // C(int64_t, rel_pc)                                         \
+    // C(base::Optional<uint32_t>, symbol_set_id)
+    //
+    // Where PERFETTO_TP_STACK_PROFILE_FRAME_DEF is |tableDefName|.
+    let pattern = `(^[ ]*//.*\n)*`;
+    pattern += `^\s*#define\\s+${tableDefName}\\s*\\(`;
+    pattern += `(.*\\\\\\s*\n)+`;
+    pattern += `.+`;
+    const r = new RegExp(pattern, 'mi');
+    const tabMatch = r.exec(hdr);
+    if (!tabMatch) {
+      console.error(`could not find table ${tableDefName}`);
+      continue;
+    }
+    tables.push(parseTableDef(tableDefName, tabMatch[0]));
+  }
+  return tables;
+}
+
+
+function genLink(table) {
+  return `[${table.name}](#${table.name})`;
+}
+
+function tableToMarkdown(table) {
+  let md = `### ${table.name}\n\n`;
+  if (table.parent) {
+    md += `_Extends ${genLink(table.parent)}_\n\n`;
+  }
+  md += table.comment + '\n\n';
+  md += 'Column | Type | Description\n';
+  md += '------ | ---- | -----------\n';
+
+  let curTable = table;
+  while (curTable) {
+    if (curTable != table) {
+      md += `||_Columns inherited from_ ${genLink(curTable)}\n`
+    }
+    for (const col of Object.values(curTable.cols)) {
+      const type = col.type + (col.optional ? '<br>`optional`' : '');
+      let description = col.comment;
+      if (col.joinTable) {
+        description += `\nJoinable with ` +
+            `[${col.joinTable}.${col.joinCol}](#${col.joinTable})`;
+      }
+      md += `${col.name} | ${type} | ${singleLineComment(description)}\n`
+    }
+    curTable = curTable.parent;
+  }
+  md += '\n\n';
+  return md;
+}
+
+function main() {
+  const inFile = argv['i'];
+  const outFile = argv['o'];
+  if (!inFile) {
+    console.error('Usage: -i hdr1.h -i hdr2.h -[-o out.md]');
+    process.exit(1);
+  }
+
+  // Can be either a string (-i single) or an array (-i one -i two).
+  const inFiles = (inFile instanceof Array) ? inFile : [inFile];
+
+  const tables = Array.prototype.concat(...inFiles.map(parseTablesInCppFile));
+
+  // Resolve parents.
+  const tablesIndex = {};    // 'TP_SCHED_SLICE_TABLE_DEF' -> table
+  const tablesByGroup = {};  // 'profilers' => [table1, table2]
+  const tablesCppName = {};  // 'StackProfileMappingTable' => table
+  const tablesByName = {};   // 'profile_mapping' => table
+  for (const table of tables) {
+    tablesIndex[table.defMacro] = table;
+    if (tablesByGroup[table.tablegroup] === undefined) {
+      tablesByGroup[table.tablegroup] = [];
+    }
+    tablesCppName[table.cppClassName] = table;
+    tablesByName[table.name] = table;
+    tablesByGroup[table.tablegroup].push(table);
+  }
+  const tableGroups = Object.keys(tablesByGroup).sort((a, b) => {
+    const keys = {'Tracks': '1', 'Events': '2', 'Misc': 'z'};
+    a = `${keys[a]}_${a}`;
+    b = `${keys[b]}_${b}`;
+    return a.localeCompare(b);
+  });
+
+  for (const table of tables) {
+    if (table.parentDefName) {
+      table.parent = tablesIndex[table.parentDefName];
+    }
+  }
+
+  // Builds a graph of the tables' relationship that can be rendererd with
+  // mermaid.js.
+  let graph = '## Tables diagram\n';
+  const mkLabel = (table) => `${table.defMacro}["${table.name}"]`;
+  for (const tableGroup of tableGroups) {
+    let gaphEdges = '';
+    let gaphLinks = '';
+    graph += `#### ${tableGroup} tables\n`;
+    graph += '```mermaid\ngraph TD\n';
+    graph += `  subgraph ${tableGroup}\n`;
+    for (const table of tablesByGroup[tableGroup]) {
+      graph += `  ${mkLabel(table)}\n`;
+      gaphLinks += `  click ${table.defMacro} "#${table.name}"\n`
+      if (table.parent) {
+        gaphEdges += ` ${mkLabel(table)} --> ${mkLabel(table.parent)}\n`
+      }
+
+      for (const col of Object.values(table.cols)) {
+        let refTable = undefined;
+        if (col.refTableCppName) {
+          refTable = tablesCppName[col.refTableCppName];
+        } else if (col.joinTable) {
+          refTable = tablesByName[col.joinTable];
+          if (!refTable) {
+            throw new Error(`Cannot find @joinable table ${col.joinTable}`);
+          }
+        }
+        if (!refTable)
+          continue;
+        gaphEdges +=
+            `  ${mkLabel(table)} -. ${col.name} .-> ${mkLabel(refTable)}\n`
+        gaphLinks += `  click ${refTable.defMacro} "#${refTable.name}"\n`
+      }
+    }
+    graph += `  end\n`;
+    graph += gaphEdges;
+    graph += gaphLinks;
+    graph += '\n```\n';
+  }
+
+  let md = graph;
+  for (const tableGroup of tableGroups) {
+    md += `## ${tableGroup}\n`
+    for (const table of tablesByGroup[tableGroup]) {
+      md += tableToMarkdown(table);
+    }
+  }
+
+  if (outFile) {
+    fs.writeFileSync(outFile, md);
+  } else {
+    console.log(md);
+  }
+  process.exit(0);
+}
+
+main();
diff --git a/infra/perfetto.dev/src/gen_stats_reference.js b/infra/perfetto.dev/src/gen_stats_reference.js
new file mode 100644
index 0000000..0f7228e
--- /dev/null
+++ b/infra/perfetto.dev/src/gen_stats_reference.js
@@ -0,0 +1,100 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Generation of reference from protos
+
+'use strict';
+
+const fs = require('fs');
+const path = require('path');
+const argv = require('yargs').argv
+
+// Removes \n due to 80col wrapping and preserves only end-of-sentence line
+// breaks.
+// TODO dedupe, this is copied from the other gen_proto file.
+function singleLineComment(comment) {
+  comment = comment || '';
+  comment = comment.trim();
+  comment = comment.replace(/\.\n/g, '<br>');
+  comment = comment.replace(/\n/g, ' ');
+  return comment;
+}
+
+function trimQuotes(s) {
+  if (s === undefined) {
+    return s;
+  }
+  const regex = /\"(.*)"/;
+  let m = regex.exec(s);
+  if (m === null) {
+    return null;
+  }
+  return m[1]
+}
+
+function parseTablesInCppFile(filePath) {
+  const hdr = fs.readFileSync(filePath, 'UTF8');
+  const regex = /^\s*F\(([\s\S]*?)\),\s*\\/mg;
+  let match;
+  let table = [];
+  while ((match = regex.exec(hdr)) !== null) {
+    let def = match[1];
+    let s = def.split(',').map(s => s.trim());
+    table.push({
+      name: s[0],
+      cardinality: s[1],
+      type: s[2],
+      scope: s[3],
+      comment: s[4] === undefined ? undefined :
+                                    s[4].split('\n').map(trimQuotes).join(' '),
+    });
+  }
+  return table;
+}
+
+
+function tableToMarkdown(table) {
+  let md = `# Trace Processor Stats\n\n`;
+  md += 'Name | Cardinality | Type | Scope | Description |\n';
+  md += '---- | ----------- | ---- | ----- | ----------- |\n';
+  for (const col of table) {
+    md += `${col.name} | ${col.cardinality} | ${col.type} | ${col.scope} | ${
+        singleLineComment(col.comment)} |\n`
+  }
+  md += '\n\n';
+  return md;
+}
+
+function main() {
+  const inFile = argv['i'];
+  const outFile = argv['o'];
+  if (!inFile) {
+    console.error('Usage: -i hdr1.h -i hdr2.h -[-o out.md]');
+    process.exit(1);
+  }
+
+  // Can be either a string (-i single) or an array (-i one -i two).
+  const inFiles = (inFile instanceof Array) ? inFile : [inFile];
+
+  const table = Array.prototype.concat(...inFiles.map(parseTablesInCppFile));
+  const md = tableToMarkdown(table);
+  if (outFile) {
+    fs.writeFileSync(outFile, md);
+  } else {
+    console.log(md);
+  }
+  process.exit(0);
+}
+
+main();
diff --git a/infra/perfetto.dev/src/markdown_render.js b/infra/perfetto.dev/src/markdown_render.js
new file mode 100644
index 0000000..ebb34d8
--- /dev/null
+++ b/infra/perfetto.dev/src/markdown_render.js
@@ -0,0 +1,220 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+const ejs = require('ejs');
+const marked = require('marked');
+const argv = require('yargs').argv
+const fs = require('fs-extra');
+const path = require('path');
+const hljs = require('highlight.js');
+
+const CS_BASE_URL =
+    'https://cs.android.com/android/platform/superproject/+/master:external/perfetto';
+
+const ROOT_DIR = path.dirname(path.dirname(path.dirname(__dirname)));
+const DOCS_DIR = path.join(ROOT_DIR, 'docs');
+
+let outDir = '';
+let curMdFile = '';
+let title = '';
+
+function hrefInDocs(href) {
+  if (href.match(/^(https?:)|^(mailto:)|^#/)) {
+    return undefined;
+  }
+  let pathFromRoot;
+  if (href.startsWith('/')) {
+    pathFromRoot = href;
+  } else {
+    curDocDir = '/' + path.relative(ROOT_DIR, path.dirname(curMdFile));
+    pathFromRoot = path.join(curDocDir, href);
+  }
+  if (pathFromRoot.startsWith('/docs/')) {
+    return pathFromRoot;
+  }
+  return undefined;
+}
+
+function assertNoDeadLink(relPathFromRoot) {
+  relPathFromRoot = relPathFromRoot.replace(/\#.*$/g, '');  // Remove #line.
+
+  // Skip check for build-time generated reference pages.
+  if (relPathFromRoot.endsWith('.autogen'))
+    return;
+
+  const fullPath = path.join(ROOT_DIR, relPathFromRoot);
+  if (!fs.existsSync(fullPath) && !fs.existsSync(fullPath + '.md')) {
+    const msg = `Dead link: ${relPathFromRoot} in ${curMdFile}`;
+    console.error(msg);
+    throw new Error(msg);
+  }
+}
+
+function renderHeading(text, level) {
+  // If the heading has an explicit ${#anchor}, use that. Otherwise infer the
+  // anchor from the text but only for h2 and h3. Note the right-hand-side TOC
+  // is dynamically generated from anchors (explicit or implicit).
+  if (level === 1 && !title) {
+    title = text;
+  }
+  let anchorId = '';
+  const explicitAnchor = /{#([\w-_.]+)}/.exec(text);
+  if (explicitAnchor) {
+    text = text.replace(explicitAnchor[0], '');
+    anchorId = explicitAnchor[1];
+  } else if (level >= 2 && level <= 3) {
+    anchorId = text.toLowerCase().replace(/[^\w]+/g, '-');
+    anchorId = anchorId.replace(/[-]+/g, '-');  // Drop consecutive '-'s.
+  }
+  let anchor = '';
+  if (anchorId) {
+    anchor = `<a name="${anchorId}" class="anchor" href="#${anchorId}"></a>`;
+  }
+  return `<h${level}>${anchor}${text}</h${level}>`;
+}
+
+function renderLink(originalLinkFn, href, title, text) {
+  if (href.startsWith('../')) {
+    throw new Error(
+        `Don\'t use relative paths in docs, always use /docs/xxx ` +
+        `or /src/xxx for both links to docs and code (${href})`)
+  }
+  const docsHref = hrefInDocs(href);
+  let sourceCodeLink = undefined;
+  if (docsHref !== undefined) {
+    // Check that the target doc exists. Skip the check on /reference/ files
+    // that are typically generated at build time.
+    assertNoDeadLink(docsHref);
+    href = docsHref.replace(/[.](md|autogen)\b/, '');
+    href = href.replace(/\/README$/, '/');
+  } else if (href.startsWith('/') && !href.startsWith('//')) {
+    // /tools/xxx -> github/tools/xxx.
+    sourceCodeLink = href;
+  }
+  if (sourceCodeLink !== undefined) {
+    // Fix up line anchors for GitHub link: #42 -> #L42.
+    sourceCodeLink = sourceCodeLink.replace(/#(\d+)$/g, '#L$1')
+    assertNoDeadLink(sourceCodeLink);
+    href = CS_BASE_URL + sourceCodeLink;
+  }
+  return originalLinkFn(href, title, text);
+}
+
+function renderCode(text, lang) {
+  if (lang === 'mermaid') {
+    return `<div class="mermaid">${text}</div>`;
+  }
+
+  let hlHtml = '';
+  if (lang) {
+    hlHtml = hljs.highlight(lang, text).value
+  } else {
+    hlHtml = hljs.highlightAuto(text).value
+  }
+  return `<code class="hljs code-block">${hlHtml}</code>`
+}
+
+function renderImage(originalImgFn, href, title, text) {
+  const docsHref = hrefInDocs(href);
+  if (docsHref !== undefined) {
+    const outFile = outDir + docsHref;
+    const outParDir = path.dirname(outFile);
+    fs.ensureDirSync(outParDir);
+    fs.copyFileSync(ROOT_DIR + docsHref, outFile);
+  }
+  if (href.endsWith('.svg')) {
+    return `<object type="image/svg+xml" data="${href}"></object>`
+  }
+  return originalImgFn(href, title, text);
+}
+
+function renderParagraph(text) {
+  let cssClass = '';
+  if (text.startsWith('NOTE:')) {
+    cssClass = 'note';
+  }
+   else if (text.startsWith('TIP:')) {
+    cssClass = 'tip';
+  }
+   else if (text.startsWith('TODO:') || text.startsWith('FIXME:')) {
+    cssClass = 'todo';
+  }
+   else if (text.startsWith('WARNING:')) {
+    cssClass = 'warning';
+  }
+   else if (text.startsWith('Summary:')) {
+    cssClass = 'summary';
+  }
+  if (cssClass != '') {
+    cssClass = ` class="callout ${cssClass}"`;
+  }
+  return `<p${cssClass}>${text}</p>\n`;
+}
+
+function render(rawMarkdown) {
+  const renderer = new marked.Renderer();
+  const originalLinkFn = renderer.link.bind(renderer);
+  const originalImgFn = renderer.image.bind(renderer);
+  renderer.link = (hr, ti, te) => renderLink(originalLinkFn, hr, ti, te);
+  renderer.image = (hr, ti, te) => renderImage(originalImgFn, hr, ti, te);
+  renderer.code = renderCode;
+  renderer.heading = renderHeading;
+  renderer.paragraph = renderParagraph;
+
+  return marked(rawMarkdown, {renderer: renderer});
+}
+
+function main() {
+  const inFile = argv['i'];
+  const outFile = argv['o'];
+  outDir = argv['odir'];
+  const templateFile = argv['t'];
+  if (!outFile || !outDir) {
+    console.error(
+        'Usage: --odir site -o out.html [-i input.md] [-t templ.html]');
+    process.exit(1);
+  }
+  curMdFile = inFile;
+
+  let markdownHtml = '';
+  if (inFile) {
+    markdownHtml = render(fs.readFileSync(inFile, 'utf8'));
+  }
+
+  if (templateFile) {
+    // TODO rename nav.html to sitemap or something more mainstream.
+    const navFilePath = path.join(outDir, 'docs', '_nav.html');
+    const fallbackTitle =
+        'Perfetto - System profiling, app tracing and trace analysis';
+    const templateData = {
+      markdown: markdownHtml,
+      title: title ? `${title} - Perfetto Tracing Docs` : fallbackTitle,
+      fileName: '/' + outFile.split('/').slice(1).join('/'),
+    };
+    if (fs.existsSync(navFilePath)) {
+      templateData['nav'] = fs.readFileSync(navFilePath, 'utf8');
+    }
+    ejs.renderFile(templateFile, templateData, (err, html) => {
+      if (err)
+        throw err;
+      fs.writeFileSync(outFile, html);
+      process.exit(0);
+    });
+  } else {
+    fs.writeFileSync(outFile, markdownHtml);
+    process.exit(0);
+  }
+}
+
+main();
diff --git a/infra/perfetto.dev/src/template_footer.html b/infra/perfetto.dev/src/template_footer.html
new file mode 100644
index 0000000..5649e26
--- /dev/null
+++ b/infra/perfetto.dev/src/template_footer.html
@@ -0,0 +1,14 @@
+
+<footer class="site-footer">
+  <p class="docs-footer-notice">
+    Except as otherwise noted, the content of this page is licensed under the
+    <a href="https://creativecommons.org/licenses/by/4.0/">Creative Commons
+    Attribution 4.0 License</a>, and code samples are licensed
+    under the <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache 2.0
+    License</a>. Java is a registered trademark of Oracle and/or its affiliates.
+  </p>
+  <ul>
+    <li><a href="//creativecommons.org/licenses/by/4.0/">Site CC BY 4.0</a></li>
+    <li><a href="//www.google.com/intl/en/policies/privacy/">Privacy</a></li>
+  </ul>
+</footer>
diff --git a/infra/perfetto.dev/src/template_header.html b/infra/perfetto.dev/src/template_header.html
new file mode 100644
index 0000000..45960d9
--- /dev/null
+++ b/infra/perfetto.dev/src/template_header.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <title><%= title %></title>
+  <meta property="og:title" content="<%= title %>">
+  <meta property="og:site_name" content="Perfetto">
+  <meta property="og:type" content="website">
+  <meta property="og:locale" content="en">
+  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
+  <link rel="canonical" href="https://perfetto.dev<%= fileName.replace('/index.html', '/') %>">
+  <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,500,600,800&display=swap" rel="stylesheet">
+  <link href="https://fonts.googleapis.com/icon?family=Roboto+Mono" rel="stylesheet">
+  <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Round" rel="stylesheet">
+  <link rel="stylesheet" href="/assets/style.css">
+  <link rel="stylesheet" href="/assets/tomorrow-night.css">
+  <link rel="shortcut icon" href="/assets/favicon.png" />
+  <script type="text/javascript" src="/assets/script.js"></script>
+</head>
+<body>
+<header class="site-header">
+  <div class="brand">
+    <a href="/">
+      <img src="/assets/brand.png">
+      <% if (fileName.startsWith('/docs/')) { %>
+      <span class="brand-docs">Docs</span>
+      <% } %>
+    </a>
+  </div>
+  <a href="#toggle" class="menu"><i class="material-icons-round">menu</i></a>
+
+  <a href="/docs/">Docs</a>
+  <a href="/docs/contributing/getting-started#community">Community</a>
+  <a href="https://ui.perfetto.dev/">Trace Viewer</a>
+  <a href="https://github.com/google/perfetto">GitHub</a>
+</header>
diff --git a/infra/perfetto.dev/src/template_index.html b/infra/perfetto.dev/src/template_index.html
new file mode 100644
index 0000000..f580f63
--- /dev/null
+++ b/infra/perfetto.dev/src/template_index.html
@@ -0,0 +1,131 @@
+<%- include('template_header.html'); -%>
+<main class="site-content">
+  <div class="section-wrapper">
+  <section class="banner">
+    <h1><span>System profiling,</span> <span>app tracing</span> <span>and trace analysis</span></h1>
+    <h2>Open-Source &centerdot; Portable &centerdot; Efficient</h2>
+    <img src="/assets/home.png" class="home-img" alt="Perfetto">
+  </section>
+  </div>
+
+  <div class="section-wrapper">
+  <section class="home-highlights">
+    <a href="#profiling"><div class="icon"></div>System Profiling</a>
+    <a href="#tracing"><div class="icon"></div>In-App Tracing</a>
+    <a href="#viewer"><div class="icon"></div>Trace Viewer</a>
+    <a href="#analysis"><div class="icon"></div>Trace Analysis</a>
+  </section>
+  </div>
+
+  <div class="section-wrapper">
+  <section class="home-section" id="profiling">
+    <img src="/assets/sys_profiling.png" alt="Profiling illustration">
+    <div>
+      <h2>System-wide profiling for Linux and Android</h2>
+      <div class="home-item">
+        <i class="material-icons-round">sort</i>
+        <h3>Linux kernel tracing</h3>
+        <p>Capture high frequency ftrace data: scheduling activity, task switching latency, CPU frequency and much more</p>
+      </div>
+      <div class="home-item">
+        <i class="material-icons-round">nfc</i>
+        <h3>Userspace profilers and extra probes</h3>
+        <p>Native heap profiling, Java heap profiling, pollers for /proc stat files</p>
+        </div>
+      <div class="home-item">
+        <i class="material-icons-round">android</i>
+        <h3>Built into Android</h3>
+        <p>Part of the platform since Android 9 Pie, runs on Linux as well</p>
+        </div>
+      <a href="/docs/quickstart/android-tracing" class="button">Get Started</a>
+    </div>
+  </section>
+  </div>
+
+  <div class="section-wrapper">
+  <section class="home-section" id="tracing">
+    <img src="/assets/app_tracing.png" alt="Tracing illustration">
+    <div>
+     <h2>App Tracing</h2>
+      <div class="home-item">
+        <i class="material-icons-round">developer_mode</i>
+        <h3>Efficient trace point instrumentation</h3>
+        <p>Log your C++ app’s activity with high throughput, low overhead trace points</p>
+      </div>
+      <div class="home-item">
+        <i class="material-icons-round">ballot</i>
+        <h3>Structured and configurable events</h3>
+        <p>Define custom protobuf messages to represent strongly-typed app-specific information, trace only what you need</p>
+        </div>
+      <div class="home-item">
+        <i class="material-icons-round">view_compact</i>
+        <h3>Integrated with system-wide tracing</h3>
+        <p>Correlate your app’s state with system-wide profiling data on the same timeline</p>
+        </div>
+      <a href="/docs/instrumentation/tracing-sdk" class="button">Get Started</a>
+    </div>
+  </section>
+  </div>
+
+  <div class="section-wrapper">
+  <section class="home-section" id="viewer">
+    <img src="/assets/ui.png" alt="Trace viewer illustration">
+    <div>
+      <h2>Trace Viewer</h2>
+      <div class="home-item">
+        <i class="material-icons-round">timeline</i>
+        <h3>Interactive trace exploration</h3>
+        <p>Record, view and process trace data with the Perfetto UI</p>
+      </div>
+      <div class="home-item">
+        <i class="material-icons-round">file_copy</i>
+        <h3>Supports popular trace format files</h3>
+        <p>TraceEvent JSON, Android systrace, ftrace text output</p>
+        </div>
+      <div class="home-item">
+        <i class="material-icons-round">offline_bolt</i>
+        <h3>Runs fully in your browser</h3>
+        <p>No server interaction involved, works even if you are offline</p>
+        </div>
+      <div>
+        <a href="//ui.perfetto.dev" class="button">Open the UI</a>
+      </div>
+    </div>
+  </section>
+  </div>
+
+  <section class="home-section" id="analysis">
+    <img src="/assets/analysis.png" alt="Trace analysis illustration">
+    <div>
+      <h2>Trace Analysis</h2>
+      <div class="home-item">
+        <i class="material-icons-round">storage</i>
+        <h3>SQL-based trace model</h3>
+        <p>Trace processor ingests traces and exposes a SQLite-based
+           interface to access the contents of the trace, both via shell and UI
+        </p>
+      </div>
+      <div class="home-item">
+        <i class="material-icons-round">speed</i>
+        <h3>Large trace analysis</h3>
+        <p>
+          Supports traces up to tenths of GBs
+          </p>
+        </div>
+      <div class="home-item">
+        <i class="material-icons-round">compare_arrows</i>
+        <h3>Interoperable</h3>
+        <p>Can import and export popular trace formats: Chromium JSON trace format, Android Systrace, ftrace, CSV</p>
+        </div>
+      <div>
+        <a href="/docs/quickstart/trace-analysis" class="button">Learn more</a>
+      </div>
+    </div>
+  </section>
+
+  <%- include('template_footer.html'); -%>
+</main>
+
+</body>
+</html>
+
diff --git a/infra/perfetto.dev/src/template_markdown.html b/infra/perfetto.dev/src/template_markdown.html
new file mode 100644
index 0000000..feeaaa5
--- /dev/null
+++ b/infra/perfetto.dev/src/template_markdown.html
@@ -0,0 +1,17 @@
+<%- include('template_header.html'); -%>
+<div class="docs">
+  <nav class="nav">
+    <%- nav %>
+  </nav>
+
+  <main class="doc" data-md-file="<%= fileName %>">
+    <%- markdown %>
+  </main>
+
+  <section class="toc"></section>
+
+  <%- include('template_footer.html'); -%>
+</div>
+
+</body>
+</html>