diff --git a/.dockerignore b/.dockerignore index 72ce590bf..75cbb64ea 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,12 +1,2 @@ -Dockerfile -build -deployment -doc -.github -.gitignore -.node-version -.travis.yml -LICENSE -README.md -node_modules -release +** +!release diff --git a/.drone.yml b/.drone.yml deleted file mode 100644 index 2d302c896..000000000 --- a/.drone.yml +++ /dev/null @@ -1,408 +0,0 @@ -kind: pipeline -type: docker -name: amd64:linux - -platform: - arch: amd64 - -steps: -- name: cache:restore - image: node:12 - commands: - - ./scripts/cacher.sh - -- name: build - image: codercom/nbin:centos - commands: - - yum install -y libxkbfile-devel libsecret-devel - - . /opt/rh/devtoolset-6/enable - - timeout 50m ./scripts/ci.bash || echo 'Timed out or failed; continuing so we can preserve cache for the next run' - -- name: cache:package - image: node:12 - commands: - - ./scripts/cacher.sh - when: - event: push - -- name: cache:push - image: plugins/gcs - settings: - source: cache-upload/ - target: codesrv-ci.cdr.sh - token: - from_secret: gcs-token - when: - event: push - -- name: test - image: node:12 - commands: - - yarn test - -- name: publish:github - image: plugins/github-release - settings: - api_key: - from_secret: github_token - files: release/*.tar.gz - draft: true - overwrite: true - title: ${DRONE_TAG} - when: - event: tag - -- name: publish:docker - image: plugins/docker - settings: - username: - from_secret: docker_user - password: - from_secret: docker_pass - repo: codercom/code-server - dockerfile: scripts/ci.dockerfile - tags: - - ${DRONE_TAG} - when: - event: tag - -- name: publish:gcs - image: plugins/gcs - settings: - source: gcs_bucket - target: codesrv-ci.cdr.sh/ - token: - from_secret: gcs-token - when: - event: tag - ---- -kind: pipeline -type: docker -name: amd64:alpine - -platform: - arch: amd64 - -steps: -- name: cache:restore - image: node:12-alpine - commands: - - ./scripts/cacher.sh - -- name: build - image: node:12-alpine - commands: - - apk add libxkbfile-dev libsecret-dev build-base git bash python - - timeout 50m ./scripts/ci.bash || echo 'Timed out or failed; continuing so we can preserve cache for the next run' - -- name: cache:package - image: node:12-alpine - commands: - - ./scripts/cacher.sh - when: - event: push - -- name: cache:push - image: plugins/gcs - settings: - source: cache-upload/ - target: codesrv-ci.cdr.sh - token: - from_secret: gcs-token - when: - event: push - -- name: test - image: node:12-alpine - commands: - - yarn test - -- name: publish:github - image: plugins/github-release - settings: - api_key: - from_secret: github_token - files: release/*.tar.gz - draft: true - overwrite: true - title: ${DRONE_TAG} - when: - event: tag - -- name: publish:gcs - image: plugins/gcs - settings: - source: gcs_bucket - target: codesrv-ci.cdr.sh/ - token: - from_secret: gcs-token - when: - event: tag - ---- -kind: pipeline -type: docker -name: arm64:linux - -platform: - arch: arm64 - -steps: -- name: cache:restore - image: node:12 - commands: - - ./scripts/cacher.sh - -- name: build - image: node:12 - commands: - - apt update && apt install -y build-essential git libsecret-1-dev libx11-dev libxkbfile-dev - - timeout 50m ./scripts/ci.bash || echo 'Timed out or failed; continuing so we can preserve cache for the next run' - -- name: cache:package - image: node:12 - commands: - - ./scripts/cacher.sh - when: - event: push - -- name: cache:push - image: plugins/gcs - settings: - source: cache-upload/ - target: codesrv-ci.cdr.sh - token: - from_secret: gcs-token - when: - event: push - -- name: test - image: node:12 - commands: - - yarn test - -- name: publish:github - image: plugins/github-release - settings: - api_key: - from_secret: github_token - files: release/*.tar.gz - draft: true - overwrite: true - title: ${DRONE_TAG} - when: - event: tag - -- name: publish:docker - image: plugins/docker - settings: - username: - from_secret: docker_user - password: - from_secret: docker_pass - repo: codercom/code-server - dockerfile: scripts/ci.dockerfile - tags: - - ${DRONE_TAG}-arm64 - when: - event: tag - -- name: publish:gcs - image: plugins/gcs - settings: - source: gcs_bucket - target: codesrv-ci.cdr.sh/ - token: - from_secret: gcs-token - when: - event: tag - ---- -kind: pipeline -type: docker -name: arm64:alpine - -platform: - arch: arm64 - -steps: -- name: cache:restore - image: node:12-alpine - commands: - - ./scripts/cacher.sh - -- name: build - image: node:12-alpine - commands: - - apk add libxkbfile-dev libsecret-dev build-base git bash python - - timeout 50m ./scripts/ci.bash || echo 'Timed out or failed; continuing so we can preserve cache for the next run' - -- name: cache:package - image: node:12-alpine - commands: - - ./scripts/cacher.sh - when: - event: push - -- name: cache:push - image: plugins/gcs - settings: - source: cache-upload/ - target: codesrv-ci.cdr.sh - token: - from_secret: gcs-token - when: - event: push - -- name: test - image: node:12-alpine - commands: - - yarn test - -- name: publish:github - image: plugins/github-release - settings: - api_key: - from_secret: github_token - files: release/*.tar.gz - draft: true - overwrite: true - title: ${DRONE_TAG} - when: - event: tag - -- name: publish:gcs - image: plugins/gcs - settings: - source: gcs_bucket - target: codesrv-ci.cdr.sh/ - token: - from_secret: gcs-token - when: - event: tag - ---- -kind: pipeline -type: docker -name: arm:linux - -platform: - arch: arm - -steps: -- name: cache:restore - image: node:12 - commands: - - ./scripts/cacher.sh - -- name: build - image: node:12 - commands: - - apt update && apt install -y build-essential git libsecret-1-dev libx11-dev libxkbfile-dev - - timeout 50m ./scripts/ci.bash || echo 'Timed out or failed; continuing so we can preserve cache for the next run' - -- name: cache:package - image: node:12 - commands: - - ./scripts/cacher.sh - when: - event: push - -- name: cache:push - image: plugins/gcs - settings: - source: cache-upload/ - target: codesrv-ci.cdr.sh - token: - from_secret: gcs-token - when: - event: push - -- name: test - image: node:12 - failure: ignore - commands: - - yarn test - -# - name: publish:github -# image: plugins/github-release -# settings: -# api_key: -# from_secret: github_token -# files: release/*.tar.gz -# draft: true -# overwrite: true -# title: ${DRONE_TAG} -# when: -# event: tag - -# - name: publish:docker -# image: plugins/docker -# settings: -# username: -# from_secret: docker_user -# password: -# from_secret: docker_pass -# repo: codercom/code-server -# dockerfile: scripts/ci.dockerfile -# tags: -# - ${DRONE_TAG}-arm -# when: -# event: tag - ---- -kind: pipeline -type: docker -name: arm:alpine - -platform: - arch: arm - -steps: -- name: cache:restore - image: node:12-alpine - commands: - - ./scripts/cacher.sh - -- name: build - image: node:12-alpine - commands: - - apk add libxkbfile-dev libsecret-dev build-base git bash python - - timeout 50m ./scripts/ci.bash || echo 'Timed out or failed; continuing so we can preserve cache for the next run' - -- name: cache:package - image: node:12-alpine - commands: - - ./scripts/cacher.sh - when: - event: push - -- name: cache:push - image: plugins/gcs - settings: - source: cache-upload/ - target: codesrv-ci.cdr.sh - token: - from_secret: gcs-token - when: - event: push - -- name: test - image: node:12-alpine - failure: ignore - commands: - - yarn test - -# - name: publish:github -# image: plugins/github-release -# failure: ignore -# settings: -# api_key: -# from_secret: github_token -# files: release/*.tar.gz -# draft: true -# overwrite: true -# title: ${DRONE_TAG} -# when: -# event: tag diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..65705d954 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,6 @@ +root = true + +[*] +indent_style = space +trim_trailing_whitespace = true +indent_size = 2 diff --git a/.eslintrc.yaml b/.eslintrc.yaml new file mode 100644 index 000000000..58112a671 --- /dev/null +++ b/.eslintrc.yaml @@ -0,0 +1,23 @@ +parser: "@typescript-eslint/parser" +env: + browser: true + es6: true # Map, etc. + mocha: true + node: true + +parserOptions: + ecmaVersion: 2018 + sourceType: module + +extends: + - eslint:recommended + - plugin:@typescript-eslint/recommended + - plugin:import/recommended + - plugin:import/typescript + - plugin:prettier/recommended + - prettier # Removes eslint rules that conflict with prettier. + - prettier/@typescript-eslint # Remove conflicts again. + +rules: + # For overloads. + no-dupe-class-members: off diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 11fee14e7..c68d7da5e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1 @@ -* @code-asher @kylecarbs -Dockerfile @nhooyr +* @code-asher @nhooyr diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 50e90f1f2..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -name: Bug Report -about: Report problems and unexpected behavior. -title: '' -labels: 'bug' -assignees: '' ---- - - - - -- `code-server` version: -- OS Version: - -## Description - - - -## Steps to Reproduce - -1. -1. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/extension_bug.md b/.github/ISSUE_TEMPLATE/extension_bug.md deleted file mode 100644 index 7c30b9479..000000000 --- a/.github/ISSUE_TEMPLATE/extension_bug.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -name: Extension Bug -about: Report problems and unexpected behavior with extensions. -title: '' -labels: 'extension-specific' -assignees: '' ---- - - - -- `code-server` version: -- OS Version: -- Extension: - -## Description - - - -## Steps to Reproduce - -1. -1. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index b33f67bc7..000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: Feature Request -about: Suggest an idea for this project. -title: '' -labels: 'feature' -assignees: '' ---- - - - - \ No newline at end of file diff --git a/.github/issue_template.md b/.github/issue_template.md new file mode 100644 index 000000000..7d73b9822 --- /dev/null +++ b/.github/issue_template.md @@ -0,0 +1,6 @@ + diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index e056fdf5f..6e29ad1b5 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,6 +1,4 @@ - - -### Describe in detail the problem you had and how this PR fixes it - -### Is there an open issue you can link to? - + diff --git a/.gitignore b/.gitignore index 0a7a0e8f5..99cc47e6d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ +*.tsbuildinfo +.cache +build +dist* +out* +release* node_modules -/build -/release -/binaries -/lib +binaries diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..9854a1b1d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/vscode"] + path = lib/vscode + url = https://github.com/microsoft/vscode diff --git a/.node-version b/.node-version deleted file mode 100644 index 65d83ce5a..000000000 --- a/.node-version +++ /dev/null @@ -1 +0,0 @@ -12.14.0 diff --git a/.npmrc b/.npmrc deleted file mode 100644 index 5fca0d518..000000000 --- a/.npmrc +++ /dev/null @@ -1 +0,0 @@ -scripts-prepend-node-path=true diff --git a/.prettierrc.yaml b/.prettierrc.yaml new file mode 100644 index 000000000..a0634116d --- /dev/null +++ b/.prettierrc.yaml @@ -0,0 +1,4 @@ +printWidth: 120 +semi: false +trailingComma: all +arrowParens: always diff --git a/.stylelintrc.yaml b/.stylelintrc.yaml new file mode 100644 index 000000000..af5c94bc4 --- /dev/null +++ b/.stylelintrc.yaml @@ -0,0 +1,2 @@ +extends: + - stylelint-config-recommended diff --git a/.travis.yml b/.travis.yml index d417dcc58..bb7a2be76 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,37 +1,53 @@ -language: node_js -node_js: - - 12.14.0 -services: - - docker +language: minimal jobs: include: - - name: "MacOS build" + - name: Test + if: tag IS blank + script: ./ci/image/run.sh "yarn && yarn vscode && ./ci/ci.sh" + deploy: null + - name: Linux Release + if: tag IS present + script: + - travis_wait 60 ./ci/image/run.sh "yarn && yarn vscode && ci/release.sh" + - ./ci/release-image/push.sh + - name: Linux ARM64 Release + if: tag IS present + script: + - travis_wait 60 ./ci/image/run.sh "yarn && yarn vscode && ci/release.sh" + - ./ci/release-image/push.sh + arch: arm64 + - name: MacOS Release + if: tag IS present os: osx - script: travis_wait 60 scripts/ci.bash + language: node_js + node_js: 12 + script: yarn && yarn vscode && travis_wait 60 ci/release.sh -git: - depth: 3 +before_deploy: + - openssl aes-256-cbc -K $encrypted_0c1654c01c97_key -iv $encrypted_0c1654c01c97_iv -in ./ci/key.json.enc -out ./ci/key.json -d deploy: - provider: releases - file_glob: true + edge: true draft: true - tag_name: "$TRAVIS_TAG" - target_commitish: "$TRAVIS_COMMIT" - name: "$TRAVIS_TAG" - skip_cleanup: true - api_key: - secure: YL/x24KjYjgYXPcJWk3FV7FGxI79Mh6gBECQEcdlf3fkLEoKFVgzHBoUNWrFPzyR4tgLyWNAgcpD9Lkme1TRWTom7UPjXcwMNyLcLa+uec7ciSAnYD9ntLTpiCuPDD1u0LtRGclSi/EHQ+F8YVq+HZJpXTsJeAmOmihma3GVbGKSZr+BRum+0YZSG4w+o4TOlYzw/4bLWS52MogZcwpjd+hemBbgXLuGU2ziKv2vEKCZFbEeA16II4x1WLI4mutDdCeh7+3aLzGLwDa49NxtsVYNjyNFF75JhCTCNA55e2YMiLz9Uq69IXe/mi5F7xUaFfhIqqLNyKBnKeEOzu3dYnc+8n3LjnQ+00PmkF05nx9kBn3UfV1kwQGh6QbyDmTtBP07rtUMyI14aeQqHjxsaVRdMnwj9Q2DjXRr8UDqESZF0rmK3pHCXS2fBhIzLE8tLVW5Heiba2pQRFMHMZW+KBE97FzcFh7is90Ait3T8enfcd/PWFPYoBejDAdjwxwOkezh5N5ZkYquEfDYuWrFi6zRFCktsruaAcA+xGtTf9oilBBzUqu8Ie+YFWH5me83xakcblJWdaW/D2rLJAJH3m6LFm8lBqyUgDX5t/etob6CpDuYHu5D1J3XINOj/+aLAcadq6qlh70PMZS3zYffUu3JlzaD2amlSHIT8b5YXFc= + tag_name: $TRAVIS_TAG + target_commitish: $TRAVIS_COMMIT + name: $TRAVIS_TAG file: - release/*.tar.gz - release/*.zip on: - repo: cdr/code-server + tags: true + - provider: gcs + edge: true + bucket: "codesrv-ci.cdr.sh" + upload_dir: "releases" + key_file: ./ci/key.json + local_dir: release-upload + on: tags: true cache: - timeout: 1000 + timeout: 600 yarn: true - directories: - - source diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 2215d1904..000000000 --- a/Dockerfile +++ /dev/null @@ -1,57 +0,0 @@ -FROM node:12.14.0 -ARG tag -ARG githubToken - -# Install VS Code's deps. These are the only two it seems we need. -RUN apt-get update && apt-get install -y \ - libxkbfile-dev \ - libsecret-1-dev - -WORKDIR /src -COPY . . - -RUN yarn \ - && DRONE_TAG="$tag" MINIFY=true BINARY=true GITHUB_TOKEN="$githubToken" ./scripts/ci.bash \ - && rm -r /src/build \ - && rm -r /src/source - -# We deploy with Ubuntu so that devs have a familiar environment. -FROM ubuntu:18.04 - -RUN apt-get update && apt-get install -y \ - openssl \ - net-tools \ - git \ - locales \ - sudo \ - dumb-init \ - vim \ - curl \ - wget \ - && rm -rf /var/lib/apt/lists/* - -RUN locale-gen en_US.UTF-8 -# We cannot use update-locale because docker will not use the env variables -# configured in /etc/default/locale so we need to set it manually. -ENV LC_ALL=en_US.UTF-8 \ - SHELL=/bin/bash - -RUN adduser --gecos '' --disabled-password coder && \ - echo "coder ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/nopasswd - -USER coder -# Create first so these directories will be owned by coder instead of root -# (workdir and mounting appear to both default to root). -RUN mkdir -p /home/coder/project \ - && mkdir -p /home/coder/.local/share/code-server - -WORKDIR /home/coder/project - -# This ensures we have a volume mounted even if the user forgot to do bind -# mount. So that they do not lose their data if they delete the container. -VOLUME [ "/home/coder/project" ] - -COPY --from=0 /src/binaries/code-server /usr/local/bin/code-server -EXPOSE 8080 - -ENTRYPOINT ["dumb-init", "code-server", "--host", "0.0.0.0"] diff --git a/LICENSE b/LICENSE.txt similarity index 100% rename from LICENSE rename to LICENSE.txt diff --git a/README.md b/README.md index 095c7a445..19e9cb80d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# code-server · [![MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/cdr/code-server/blob/master/LICENSE) [!["Latest Release"](https://img.shields.io/github/release/cdr/code-server.svg)](https://github.com/cdr/code-server/releases/latest) [![Build Status](https://img.shields.io/travis/com/cdr/code-server/master)](https://github.com/cdr/code-server) +# code-server `code-server` is [VS Code](https://github.com/Microsoft/vscode) running on a remote server, accessible through the browser. @@ -9,14 +9,14 @@ Try it out: docker run -it -p 127.0.0.1:8080:8080 -v "$PWD:/home/coder/project" codercom/code-server ``` -- **Consistent environment:** Code on your Chromebook, tablet, and laptop with a - consistent dev environment. develop more easily for Linux if you have a - Windows or Mac, and pick up where you left off when switching workstations. +- **Code anywhere:** Code on your Chromebook, tablet, and laptop with a + consistent dev environment. Develop on a Linux machine and pick up from any + device with a web browser. - **Server-powered:** Take advantage of large cloud servers to speed up tests, compilations, downloads, and more. Preserve battery life when you're on the go since all intensive computation runs on your server. -![Screenshot](/doc/assets/ide.gif) +![Example gif](/doc/assets/code-server.gif) ## Getting Started @@ -25,171 +25,30 @@ docker run -it -p 127.0.0.1:8080:8080 -v "$PWD:/home/coder/project" codercom/cod - 64-bit host. - At least 1GB of RAM. - 2 cores or more are recommended (1 core works but not optimally). -- Secure connection over HTTPS or localhost (required for service workers). +- Secure connection over HTTPS or localhost (required for service workers and + clipboard support). - For Linux: GLIBC 2.17 or later and GLIBCXX 3.4.15 or later. -- Docker (for Docker versions of `code-server`). ### Run over SSH Use [sshcode](https://github.com/codercom/sshcode) for a simple setup. -### Docker - -See the Docker one-liner mentioned above. Dockerfile is at [/Dockerfile](/Dockerfile). - -To debug Golang using the -[ms-vscode-go extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.Go), -you need to add `--security-opt seccomp=unconfined` to your `docker run` -arguments when launching code-server with Docker. See -[#725](https://github.com/cdr/code-server/issues/725) for details. - ### Digital Ocean [![Create a Droplet](./doc/assets/droplet.svg)](https://marketplace.digitalocean.com/apps/code-server?action=deploy) -### Binaries +### Releases -1. [Download a binary](https://github.com/cdr/code-server/releases). (Linux and - OS X supported. Windows coming soon) -2. Unpack the downloaded file then run the binary. +1. [Download a release](https://github.com/cdr/code-server/releases). (Linux and + OS X supported. Windows support planned.) +2. Unpack the downloaded release then run the included `code-server` script. 3. In your browser navigate to `localhost:8080`. -- For self-hosting and other information see [doc/quickstart.md](doc/quickstart.md). -- For hosting on cloud platforms see [doc/deploy.md](doc/deploy.md). +## FAQ -### Build - -See -[VS Code's prerequisites](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#prerequisites) -before building. - -```shell -export OUT=/path/to/output/build # Optional if only building. Required if also developing. -yarn build $vscodeVersion $codeServerVersion # See scripts/ci.bash for the VS Code version to use. - # The code-server version can be anything you want. -node /path/to/output/build/out/vs/server/main.js # You can run the built JavaScript with Node. -yarn binary $vscodeVersion $codeServerVersion # Or you can package it into a binary. -``` - -## Security - -### Authentication -By default `code-server` enables password authentication using a randomly -generated password. You can set the `PASSWORD` environment variable to use your -own instead or use `--auth none` to disable password authentication. - -Do not expose `code-server` to the open internet without some form of -authentication. - -### Encrypting traffic with HTTPS -If you aren't doing SSL termination elsewhere you can directly give -`code-server` a certificate with `code-server --cert` followed by the path to -your certificate. Additionally, you can use certificate keys with `--cert-key` -followed by the path to your key. If you pass `--cert` without any path -`code-server` will generate a self-signed certificate. - -If `code-server` has been passed a certificate it will also respond to HTTPS -requests and will redirect all HTTP requests to HTTPS. Otherwise it will respond -only to HTTP requests. - -You can use [Let's Encrypt](https://letsencrypt.org/) to get an SSL certificate -for free. - -Do not expose `code-server` to the open internet without SSL, whether built-in -or through a proxy. - -## Known Issues - -- Creating custom VS Code extensions and debugging them doesn't work. -- Extension profiling and tips are currently disabled. - -## Future - -- **Stay up to date!** Get notified about new releases of code-server. - ![Screenshot](/doc/assets/release.gif) -- Windows support. -- Electron and Chrome OS applications to bridge the gap between local<->remote. -- Run VS Code unit tests against our builds to ensure features work as expected. - -## Extensions - -code-server does not provide access to the official -[Visual Studio Marketplace](https://marketplace.visualstudio.com/vscode). Instead, -Coder has created a custom extension marketplace that we manage for open-source -extensions. If you want to use an extension with code-server that we do not have -in our marketplace please look for a release in the extension’s repository, -contact us to see if we have one in the works or, if you build an extension -locally from open source, you can copy it to the `extensions` folder. If you -build one locally from open-source please contribute it to the project and let -us know so we can give you props! If you have your own custom marketplace, it is -possible to point code-server to it by setting the `SERVICE_URL` and `ITEM_URL` -environment variables. - -## Telemetry - -Use the `--disable-telemetry` flag to completely disable telemetry. We use the -data collected to improve code-server. - -## Contributing - -### Development - -See -[VS Code's prerequisites](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#prerequisites) -before developing. - -```shell -git clone https://github.com/microsoft/vscode -cd vscode -git checkout ${vscodeVersion} # See scripts/ci.bash for the version to use. -yarn -git clone https://github.com/cdr/code-server src/vs/server -cd src/vs/server -yarn -yarn patch:apply -yarn watch -# Wait for the initial compilation to complete (it will say "Finished compilation"). -# Run the next command in another shell. -yarn start -# Visit http://localhost:8080 -``` - -If you run into issues about a different version of Node being used, try running -`npm rebuild` in the VS Code directory. - -### Upgrading VS Code - -We patch VS Code to provide and fix some functionality. As the web portion of VS -Code matures, we'll be able to shrink and maybe even entirely eliminate our -patch. In the meantime, however, upgrading the VS Code version requires ensuring -that the patch still applies and has the intended effects. - -To generate a new patch, **stage all the changes** you want to be included in -the patch in the VS Code source, then run `yarn patch:generate` in this -directory. - -Our changes include: - -- Allow multiple extension directories (both user and built-in). -- Modify the loader, websocket, webview, service worker, and asset requests to - use the URL of the page as a base (and TLS if necessary for the websocket). -- Send client-side telemetry through the server. -- Make changing the display language work. -- Make it possible for us to load code on the client. -- Make extensions work in the browser. -- Fix getting permanently disconnected when you sleep or hibernate for a while. -- Make it possible to automatically update the binary. - -## License - -[MIT](LICENSE) +See [./doc/FAQ.md](./doc/FAQ.md). ## Enterprise Visit [our enterprise page](https://coder.com) for more information about our -enterprise offering. - -## Commercialization - -If you would like to commercialize code-server, please contact -contact@coder.com. +enterprise offerings. diff --git a/ci/build.ts b/ci/build.ts new file mode 100644 index 000000000..649bbe05e --- /dev/null +++ b/ci/build.ts @@ -0,0 +1,364 @@ +import * as cp from "child_process" +import * as fs from "fs-extra" +import Bundler from "parcel-bundler" +import * as path from "path" +import * as util from "util" + +enum Task { + Build = "build", + Watch = "watch", +} + +class Builder { + private readonly rootPath = path.resolve(__dirname, "..") + private readonly vscodeSourcePath = path.join(this.rootPath, "lib/vscode") + private readonly buildPath = path.join(this.rootPath, "build") + private readonly codeServerVersion: string + private currentTask?: Task + + public constructor() { + this.ensureArgument("rootPath", this.rootPath) + this.codeServerVersion = this.ensureArgument( + "codeServerVersion", + process.env.VERSION || require(path.join(this.rootPath, "package.json")).version, + ) + } + + public run(task: Task | undefined): void { + this.currentTask = task + this.doRun(task).catch((error) => { + console.error(error.message) + process.exit(1) + }) + } + + private async task(message: string, fn: () => Promise): Promise { + const time = Date.now() + this.log(`${message}...`, true) + try { + const t = await fn() + process.stdout.write(`took ${Date.now() - time}ms\n`) + return t + } catch (error) { + process.stdout.write("failed\n") + throw error + } + } + + /** + * Writes to stdout with an optional newline. + */ + private log(message: string, skipNewline = false): void { + process.stdout.write(`[${this.currentTask || "default"}] ${message}`) + if (!skipNewline) { + process.stdout.write("\n") + } + } + + private async doRun(task: Task | undefined): Promise { + if (!task) { + throw new Error("No task provided") + } + + switch (task) { + case Task.Watch: + return this.watch() + case Task.Build: + return this.build() + default: + throw new Error(`No task matching "${task}"`) + } + } + + /** + * Make sure the argument is set. Display the value if it is. + */ + private ensureArgument(name: string, arg?: string): string { + if (!arg) { + throw new Error(`${name} is missing`) + } + this.log(`${name} is "${arg}"`) + return arg + } + + /** + * Build VS Code and code-server. + */ + private async build(): Promise { + process.env.NODE_OPTIONS = "--max-old-space-size=32384 " + (process.env.NODE_OPTIONS || "") + process.env.NODE_ENV = "production" + + await this.task("cleaning up old build", async () => { + if (!process.env.SKIP_VSCODE) { + return fs.remove(this.buildPath) + } + // If skipping VS Code, keep the existing build if any. + try { + const files = await fs.readdir(this.buildPath) + return Promise.all(files.filter((f) => f !== "lib").map((f) => fs.remove(path.join(this.buildPath, f)))) + } catch (error) { + if (error.code !== "ENOENT") { + throw error + } + } + }) + + const commit = require(path.join(this.vscodeSourcePath, "build/lib/util")).getVersion(this.rootPath) as string + if (!process.env.SKIP_VSCODE) { + await this.buildVscode(commit) + } else { + this.log("skipping vs code build") + } + await this.buildCodeServer(commit) + + this.log(`final build: ${this.buildPath}`) + } + + private async buildCodeServer(commit: string): Promise { + await this.task("building code-server", async () => { + return util.promisify(cp.exec)("tsc --outDir ./out-build --tsBuildInfoFile ./.prod.tsbuildinfo", { + cwd: this.rootPath, + }) + }) + + await this.task("bundling code-server", async () => { + return this.createBundler("dist-build", commit).bundle() + }) + + await this.task("copying code-server into build directory", async () => { + await fs.mkdirp(this.buildPath) + await Promise.all([ + fs.copy(path.join(this.rootPath, "out-build"), path.join(this.buildPath, "out")), + fs.copy(path.join(this.rootPath, "dist-build"), path.join(this.buildPath, "dist")), + // For source maps and images. + fs.copy(path.join(this.rootPath, "src"), path.join(this.buildPath, "src")), + ]) + }) + + await this.copyDependencies("code-server", this.rootPath, this.buildPath, { + commit, + version: this.codeServerVersion, + }) + } + + private async buildVscode(commit: string): Promise { + await this.task("building vs code", () => { + return util.promisify(cp.exec)("yarn gulp compile-build", { cwd: this.vscodeSourcePath }) + }) + + await this.task("building builtin extensions", async () => { + const exists = await fs.pathExists(path.join(this.vscodeSourcePath, ".build/extensions")) + if (exists) { + process.stdout.write("already built, skipping...") + } else { + await util.promisify(cp.exec)("yarn gulp compile-extensions-build", { cwd: this.vscodeSourcePath }) + } + }) + + await this.task("optimizing vs code", async () => { + return util.promisify(cp.exec)("yarn gulp optimize --gulpfile ./coder.js", { cwd: this.vscodeSourcePath }) + }) + + if (process.env.MINIFY) { + await this.task("minifying vs code", () => { + return util.promisify(cp.exec)("yarn gulp minify --gulpfile ./coder.js", { cwd: this.vscodeSourcePath }) + }) + } + + const vscodeBuildPath = path.join(this.buildPath, "lib/vscode") + await this.task("copying vs code into build directory", async () => { + await fs.mkdirp(path.join(vscodeBuildPath, "resources/linux")) + await Promise.all([ + fs.move( + path.join(this.vscodeSourcePath, `out-vscode${process.env.MINIFY ? "-min" : ""}`), + path.join(vscodeBuildPath, "out"), + ), + fs.copy(path.join(this.vscodeSourcePath, ".build/extensions"), path.join(vscodeBuildPath, "extensions")), + fs.copy( + path.join(this.vscodeSourcePath, "resources/linux/code.png"), + path.join(vscodeBuildPath, "resources/linux/code.png"), + ), + ]) + }) + + await this.copyDependencies("vs code", this.vscodeSourcePath, vscodeBuildPath, { + commit, + date: new Date().toISOString(), + }) + } + + private async copyDependencies(name: string, sourcePath: string, buildPath: string, merge: object): Promise { + await this.task(`copying ${name} dependencies`, async () => { + return Promise.all( + ["node_modules", "package.json", "yarn.lock"].map((fileName) => { + return fs.copy(path.join(sourcePath, fileName), path.join(buildPath, fileName)) + }), + ) + }) + + const fileName = name === "code-server" ? "package" : "product" + await this.task(`writing final ${name} ${fileName}.json`, async () => { + const json = JSON.parse(await fs.readFile(path.join(sourcePath, `${fileName}.json`), "utf8")) + return fs.writeFile( + path.join(buildPath, `${fileName}.json`), + JSON.stringify( + { + ...json, + ...merge, + }, + null, + 2, + ), + ) + }) + + if (process.env.MINIFY) { + await this.task(`restricting ${name} to production dependencies`, async () => { + await util.promisify(cp.exec)("yarn --production --ignore-scripts", { cwd: buildPath }) + }) + } + } + + private async watch(): Promise { + let server: cp.ChildProcess | undefined + const restartServer = (): void => { + if (server) { + server.kill() + } + const s = cp.fork(path.join(this.rootPath, "out/node/entry.js"), process.argv.slice(3)) + console.log(`[server] spawned process ${s.pid}`) + s.on("exit", () => console.log(`[server] process ${s.pid} exited`)) + server = s + } + + const vscode = cp.spawn("yarn", ["watch"], { cwd: this.vscodeSourcePath }) + const tsc = cp.spawn("tsc", ["--watch", "--pretty", "--preserveWatchOutput"], { cwd: this.rootPath }) + const bundler = this.createBundler() + + const cleanup = (code?: number | null): void => { + this.log("killing vs code watcher") + vscode.removeAllListeners() + vscode.kill() + + this.log("killing tsc") + tsc.removeAllListeners() + tsc.kill() + + if (server) { + this.log("killing server") + server.removeAllListeners() + server.kill() + } + + this.log("killing bundler") + process.exit(code || 0) + } + + process.on("SIGINT", () => cleanup()) + process.on("SIGTERM", () => cleanup()) + + vscode.on("exit", (code) => { + this.log("vs code watcher terminated unexpectedly") + cleanup(code) + }) + tsc.on("exit", (code) => { + this.log("tsc terminated unexpectedly") + cleanup(code) + }) + const bundle = bundler.bundle().catch(() => { + this.log("parcel watcher terminated unexpectedly") + cleanup(1) + }) + bundler.on("buildEnd", () => { + console.log("[parcel] bundled") + }) + bundler.on("buildError", (error) => { + console.error("[parcel]", error) + }) + + vscode.stderr.on("data", (d) => process.stderr.write(d)) + tsc.stderr.on("data", (d) => process.stderr.write(d)) + + // From https://github.com/chalk/ansi-regex + const pattern = [ + "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", + "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))", + ].join("|") + const re = new RegExp(pattern, "g") + + /** + * Split stdout on newlines and strip ANSI codes. + */ + const onLine = (proc: cp.ChildProcess, callback: (strippedLine: string, originalLine: string) => void): void => { + let buffer = "" + if (!proc.stdout) { + throw new Error("no stdout") + } + proc.stdout.setEncoding("utf8") + proc.stdout.on("data", (d) => { + const data = buffer + d + const split = data.split("\n") + const last = split.length - 1 + + for (let i = 0; i < last; ++i) { + callback(split[i].replace(re, ""), split[i]) + } + + // The last item will either be an empty string (the data ended with a + // newline) or a partial line (did not end with a newline) and we must + // wait to parse it until we get a full line. + buffer = split[last] + }) + } + + let startingVscode = false + let startedVscode = false + onLine(vscode, (line, original) => { + console.log("[vscode]", original) + // Wait for watch-client since "Finished compilation" will appear multiple + // times before the client starts building. + if (!startingVscode && line.includes("Starting watch-client")) { + startingVscode = true + } else if (startingVscode && line.includes("Finished compilation")) { + if (startedVscode) { + bundle.then(restartServer) + } + startedVscode = true + } + }) + + onLine(tsc, (line, original) => { + // tsc outputs blank lines; skip them. + if (line !== "") { + console.log("[tsc]", original) + } + if (line.includes("Watching for file changes")) { + bundle.then(restartServer) + } + }) + } + + private createBundler(out = "dist", commit?: string): Bundler { + return new Bundler( + [ + path.join(this.rootPath, "src/browser/pages/app.ts"), + path.join(this.rootPath, "src/browser/register.ts"), + path.join(this.rootPath, "src/browser/serviceWorker.ts"), + ], + { + cache: true, + cacheDir: path.join(this.rootPath, ".cache"), + detailedReport: true, + minify: !!process.env.MINIFY, + hmr: false, + logLevel: 1, + outDir: path.join(this.rootPath, out), + publicUrl: `/static/${commit || "development"}/dist`, + target: "browser", + }, + ) + } +} + +const builder = new Builder() +builder.run(process.argv[2] as Task) diff --git a/ci/ci.sh b/ci/ci.sh new file mode 100755 index 000000000..79d489f02 --- /dev/null +++ b/ci/ci.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -euo pipefail + +main() { + cd "$(dirname "$0")/.." + + yarn fmt + yarn lint + yarn test +} + +main "$@" diff --git a/ci/clean.sh b/ci/clean.sh new file mode 100755 index 000000000..df4a6bebf --- /dev/null +++ b/ci/clean.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +set -euo pipefail + +main() { + git clean -Xffd + git submodule foreach --recursive git clean -xffd + git submodule foreach --recursive git reset --hard +} + +main "$@" diff --git a/ci/code-server.sh b/ci/code-server.sh new file mode 100755 index 000000000..4e234de97 --- /dev/null +++ b/ci/code-server.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env sh +# code-server.sh -- Run code-server with the bundled Node binary. + +cd "$(dirname "$0")" || exit 1 + +./node ./out/node/entry.js "$@" diff --git a/ci/fmt.sh b/ci/fmt.sh new file mode 100755 index 000000000..722395bcc --- /dev/null +++ b/ci/fmt.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +set -euo pipefail + +main() { + shfmt -i 2 -w -s -sr $(git ls-files "*.sh") + + local prettierExts + prettierExts=( + "*.js" + "*.ts" + "*.tsx" + "*.html" + "*.json" + "*.css" + "*.md" + "*.toml" + "*.yaml" + "*.yml" + ) + prettier --write --loglevel=warn $(git ls-files "${prettierExts[@]}") + + if [[ ${CI-} && $(git ls-files --other --modified --exclude-standard) ]]; then + echo "Files need generation or are formatted incorrectly:" + git -c color.ui=always status | grep --color=no '\[31m' + echo "Please run the following locally:" + echo " yarn fmt" + exit 1 + fi +} + +main "$@" diff --git a/ci/image/Dockerfile b/ci/image/Dockerfile new file mode 100644 index 000000000..0be870a7a --- /dev/null +++ b/ci/image/Dockerfile @@ -0,0 +1,10 @@ +FROM node:12.14.0 + +RUN apt-get update && apt-get install -y \ + libxkbfile-dev \ + libx11-dev \ + libsecret-1-dev + +RUN curl -L https://github.com/mvdan/sh/releases/download/v3.0.1/shfmt_v3.0.1_linux_amd64 > /usr/local/bin/shfmt && chmod +x /usr/local/bin/shfmt + +ENTRYPOINT ["/bin/bash", "-c"] diff --git a/ci/image/run.sh b/ci/image/run.sh new file mode 100755 index 000000000..6a1831b0f --- /dev/null +++ b/ci/image/run.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +set -euo pipefail + +main() { + cd "$(dirname "$0")/../.." + + docker build ci/image + imageTag="$(docker build -q ci/image)" + docker run -t --rm -e CI -e GITHUB_TOKEN -e TRAVIS_TAG -v "$(yarn cache dir):/usr/local/share/.cache/yarn/v6" -v "$PWD:/repo" -w /repo "$imageTag" "$*" +} + +main "$@" diff --git a/ci/key.json.enc b/ci/key.json.enc new file mode 100644 index 000000000..9fa3a8f64 Binary files /dev/null and b/ci/key.json.enc differ diff --git a/ci/lib.sh b/ci/lib.sh new file mode 100644 index 000000000..7118ddd09 --- /dev/null +++ b/ci/lib.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -euo pipefail + +set_version() { + local code_server_version=${VERSION:-${TRAVIS_TAG:-}} + if [[ -z $code_server_version ]]; then + code_server_version=$(grep version ./package.json | head -1 | awk -F: '{ print $2 }' | sed 's/[",]//g' | tr -d '[:space:]') + fi + export VERSION=$code_server_version +} diff --git a/ci/lint.sh b/ci/lint.sh new file mode 100755 index 000000000..d6665edc3 --- /dev/null +++ b/ci/lint.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +set -euo pipefail + +main() { + eslint --max-warnings=0 --fix $(git ls-files "*.ts" "*.tsx" "*.js") + stylelint $(git ls-files "*.css") + tsc --noEmit +} + +main "$@" diff --git a/ci/release-image/Dockerfile b/ci/release-image/Dockerfile new file mode 100644 index 000000000..dcd2be3d0 --- /dev/null +++ b/ci/release-image/Dockerfile @@ -0,0 +1,38 @@ +FROM debian:10 + +RUN apt-get update +RUN apt-get install -y curl + +# https://wiki.debian.org/Locale#Manually +RUN apt-get install -y locales +RUN sed -i "s/# en_US.UTF-8/en_US.UTF-8/" /etc/locale.gen +RUN locale-gen +ENV LANG=en_US.UTF-8 + +RUN chsh -s /bin/bash +ENV SHELL=/bin/bash + +RUN apt-get install -y dumb-init sudo +RUN apt-get install -y man procps vim nano htop ssh git + +RUN adduser --gecos '' --disabled-password coder && \ + echo "coder ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/nopasswd + +RUN curl -SsL https://github.com/boxboat/fixuid/releases/download/v0.4/fixuid-0.4-linux-amd64.tar.gz | tar -C /usr/local/bin -xzf - && \ + chown root:root /usr/local/bin/fixuid && \ + chmod 4755 /usr/local/bin/fixuid && \ + mkdir -p /etc/fixuid && \ + printf "user: coder\ngroup: coder\n" > /etc/fixuid/config.yml + +RUN rm -rf /var/lib/apt/lists/* + +COPY release/code-server*.tar.gz /tmp +RUN cd /tmp && tar -xzf code-server*.tar.gz && rm code-server*.tar.gz && \ + mv code-server* /usr/local/lib/code-server && \ + sed 's/\$0/\/usr\/local\/lib\/code-server\/code-server/g' /usr/local/lib/code-server/code-server > /usr/local/bin/code-server && \ + chmod +x /usr/local/bin/code-server + +EXPOSE 8080 +USER coder +WORKDIR /home/coder +ENTRYPOINT ["dumb-init", "fixuid", "-q", "/usr/local/bin/code-server", "--host", "0.0.0.0", "."] diff --git a/ci/release-image/push.sh b/ci/release-image/push.sh new file mode 100755 index 000000000..6baf73320 --- /dev/null +++ b/ci/release-image/push.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +set -euo pipefail + +main() { + cd "$(dirname "$0")/../.." + source ./ci/lib.sh + set_version + + if [[ ${CI:-} ]]; then + echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin + fi + + imageTag="codercom/code-server:$VERSION" + if [[ ${TRAVIS_CPU_ARCH:-} == "arm64" ]]; then + imageTag+="-arm64" + fi + docker build -t "$imageTag" -f ./ci/release-image/Dockerfile . + docker push codercom/code-server +} + +main "$@" diff --git a/ci/release.sh b/ci/release.sh new file mode 100755 index 000000000..013f58f49 --- /dev/null +++ b/ci/release.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +# ci.bash -- Build code-server in the CI. + +set -euo pipefail + +function package() { + local target + target=$(uname | tr '[:upper:]' '[:lower:]') + if [[ $target == "linux" ]]; then + # Alpine's ldd doesn't have a version flag but if you use an invalid flag + # (like --version) it outputs the version to stderr and exits with 1. + local ldd_output + ldd_output=$(ldd --version 2>&1 || true) + if echo "$ldd_output" | grep -iq musl; then + target="alpine" + fi + fi + + local arch + arch="$(uname -m)" + + echo -n "Creating release..." + + cp "$(command -v node)" ./build + cp README.md ./build + cp LICENSE.txt ./build + cp ./lib/vscode/ThirdPartyNotices.txt ./build + cp ./ci/code-server.sh ./build/code-server + + local archive_name="code-server-$VERSION-$target-$arch" + mkdir -p ./release + + local ext + if [[ $target == "linux" ]]; then + ext=".tar.gz" + tar -czf "release/$archive_name$ext" --transform "s/^\.\/build/$archive_name/" ./build + else + mv ./build "./$archive_name" + ext=".zip" + zip -r "release/$archive_name$ext" ./code-server + mv "./$archive_name" ./build + fi + + echo "done (release/$archive_name)" + + mkdir -p "./release-upload/$VERSION" + cp "./release/$archive_name$ext" "./release-upload/$VERSION/$target-$arch.tar.gz" + mkdir -p "./release-upload/latest" + cp "./release/$archive_name$ext" "./release-upload/latest/$target-$arch.tar.gz" +} + +# This script assumes that yarn has already ran. +function build() { + # Always minify and package on CI. + if [[ ${CI:-} ]]; then + export MINIFY="true" + fi + + yarn build +} + +function main() { + cd "$(dirname "${0}")/.." + source ./ci/lib.sh + + set_version + + build + + if [[ ${CI:-} ]]; then + package + fi +} + +main "$@" diff --git a/ci/tsconfig.json b/ci/tsconfig.json new file mode 100644 index 000000000..5197ce276 --- /dev/null +++ b/ci/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["./**/*.ts"] +} diff --git a/ci/vscode.patch b/ci/vscode.patch new file mode 100644 index 000000000..8ffbca902 --- /dev/null +++ b/ci/vscode.patch @@ -0,0 +1,3226 @@ +diff --git a/.gitignore b/.gitignore +index 160c42ed74..0d544c495c 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -23,7 +23,6 @@ out-vscode-reh-web-min/ + out-vscode-reh-web-pkg/ + out-vscode-web/ + out-vscode-web-min/ +-src/vs/server + resources/server + build/node_modules + coverage/ +diff --git a/coder.js b/coder.js +new file mode 100644 +index 0000000000..6aee0e46bc +--- /dev/null ++++ b/coder.js +@@ -0,0 +1,70 @@ ++// This must be ran from VS Code's root. ++const gulp = require("gulp"); ++const path = require("path"); ++const _ = require("underscore"); ++const buildfile = require("./src/buildfile"); ++const common = require("./build/lib/optimize"); ++const util = require("./build/lib/util"); ++const deps = require("./build/dependencies"); ++ ++const vscodeEntryPoints = _.flatten([ ++ buildfile.entrypoint("vs/workbench/workbench.web.api"), ++ buildfile.entrypoint("vs/server/entry"), ++ buildfile.base, ++ buildfile.workbenchWeb, ++ buildfile.workerExtensionHost, ++ buildfile.keyboardMaps, ++ buildfile.entrypoint("vs/platform/files/node/watcher/unix/watcherApp", ["vs/css", "vs/nls"]), ++ buildfile.entrypoint("vs/platform/files/node/watcher/nsfw/watcherApp", ["vs/css", "vs/nls"]), ++ buildfile.entrypoint("vs/workbench/services/extensions/node/extensionHostProcess", ["vs/css", "vs/nls"]), ++]); ++ ++const vscodeResources = [ ++ "out-build/vs/server/fork.js", ++ "out-build/vs/server/node/uriTransformer.js", ++ "!out-build/vs/server/doc/**", ++ "out-build/vs/workbench/services/extensions/worker/extensionHostWorkerMain.js", ++ "out-build/bootstrap.js", ++ "out-build/bootstrap-fork.js", ++ "out-build/bootstrap-amd.js", ++ "out-build/paths.js", ++ 'out-build/vs/**/*.{svg,png,html}', ++ "!out-build/vs/code/browser/workbench/*.html", ++ '!out-build/vs/code/electron-browser/**', ++ "out-build/vs/base/common/performance.js", ++ "out-build/vs/base/node/languagePacks.js", ++ "out-build/vs/base/browser/ui/octiconLabel/octicons/**", ++ "out-build/vs/base/browser/ui/codiconLabel/codicon/**", ++ "out-build/vs/workbench/browser/media/*-theme.css", ++ "out-build/vs/workbench/contrib/debug/**/*.json", ++ "out-build/vs/workbench/contrib/externalTerminal/**/*.scpt", ++ "out-build/vs/workbench/contrib/webview/browser/pre/*.js", ++ "out-build/vs/**/markdown.css", ++ "out-build/vs/workbench/contrib/tasks/**/*.json", ++ "out-build/vs/platform/files/**/*.md", ++ "!**/test/**" ++]; ++ ++const rootPath = __dirname; ++const nodeModules = ["electron", "original-fs"] ++ .concat(_.uniq(deps.getProductionDependencies(rootPath).map((d) => d.name))) ++ .concat(_.uniq(deps.getProductionDependencies(path.join(rootPath, "src/vs/server")).map((d) => d.name))) ++ .concat(Object.keys(process.binding("natives")).filter((n) => !/^_|\//.test(n))); ++ ++gulp.task("optimize", gulp.series( ++ util.rimraf("out-vscode"), ++ common.optimizeTask({ ++ src: "out-build", ++ entryPoints: vscodeEntryPoints, ++ resources: vscodeResources, ++ loaderConfig: common.loaderConfig(nodeModules), ++ out: "out-vscode", ++ inlineAmdImages: true, ++ bundleInfo: undefined ++ }), ++)); ++ ++gulp.task("minify", gulp.series( ++ util.rimraf("out-vscode-min"), ++ common.minifyTask("out-vscode") ++)); +diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json +index 8ac6b2806c..8562a284db 100644 +--- a/extensions/vscode-api-tests/package.json ++++ b/extensions/vscode-api-tests/package.json +@@ -121,7 +121,7 @@ + "@types/node": "^12.11.7", + "mocha-junit-reporter": "^1.17.0", + "mocha-multi-reporters": "^1.1.7", +- "typescript": "^1.6.2", ++ "typescript": "3.7.2", + "vscode": "1.1.5" + } + } +diff --git a/extensions/vscode-api-tests/yarn.lock b/extensions/vscode-api-tests/yarn.lock +index 2d8b725ff2..a8d93a17ca 100644 +--- a/extensions/vscode-api-tests/yarn.lock ++++ b/extensions/vscode-api-tests/yarn.lock +@@ -1855,10 +1855,10 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +-typescript@^1.6.2: +- version "1.8.10" +- resolved "https://registry.yarnpkg.com/typescript/-/typescript-1.8.10.tgz#b475d6e0dff0bf50f296e5ca6ef9fbb5c7320f1e" +- integrity sha1-tHXW4N/wv1DyluXKbvn7tccyDx4= ++typescript@3.7.2: ++ version "3.7.2" ++ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb" ++ integrity sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ== + + unique-stream@^2.0.2: + version "2.2.1" +diff --git a/package.json b/package.json +index fde05321d2..2427e7d4ae 100644 +--- a/package.json ++++ b/package.json +@@ -32,6 +32,9 @@ + "eslint": "eslint -c .eslintrc.json --rulesdir ./build/lib/eslint --ext .ts --ext .js ./src/vs ./extensions" + }, + "dependencies": { ++ "@coder/logger": "^1.1.12", ++ "@coder/node-browser": "^1.0.8", ++ "@coder/requirefs": "^1.1.5", + "applicationinsights": "1.0.8", + "chokidar": "3.2.3", + "graceful-fs": "4.1.11", +diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts +index a68e020f9f..c31e7befa3 100644 +--- a/src/vs/base/common/network.ts ++++ b/src/vs/base/common/network.ts +@@ -88,16 +88,17 @@ class RemoteAuthoritiesImpl { + if (host && host.indexOf(':') !== -1) { + host = `[${host}]`; + } +- const port = this._ports[authority]; ++ // const port = this._ports[authority]; + const connectionToken = this._connectionTokens[authority]; + let query = `path=${encodeURIComponent(uri.path)}`; + if (typeof connectionToken === 'string') { + query += `&tkn=${encodeURIComponent(connectionToken)}`; + } ++ // NOTE@coder: Changed this to work against the current path. + return URI.from({ + scheme: platform.isWeb ? this._preferredWebSchema : Schemas.vscodeRemoteResource, +- authority: `${host}:${port}`, +- path: `/vscode-remote-resource`, ++ authority: window.location.host, ++ path: `${window.location.pathname.replace(/\/+$/, '')}/vscode-remote-resource`, + query + }); + } +diff --git a/src/vs/base/common/platform.ts b/src/vs/base/common/platform.ts +index 5a631e0b39..4114bd9287 100644 +--- a/src/vs/base/common/platform.ts ++++ b/src/vs/base/common/platform.ts +@@ -59,6 +59,17 @@ if (typeof navigator === 'object' && !isElectronRenderer) { + _isWeb = true; + _locale = navigator.language; + _language = _locale; ++ // NOTE@coder: Make languages work. ++ const el = typeof document !== 'undefined' && document.getElementById('vscode-remote-nls-configuration'); ++ const rawNlsConfig = el && el.getAttribute('data-settings'); ++ if (rawNlsConfig) { ++ try { ++ const nlsConfig: NLSConfig = JSON.parse(rawNlsConfig); ++ _locale = nlsConfig.locale; ++ _translationsConfigFile = nlsConfig._translationsConfigFile; ++ _language = nlsConfig.availableLanguages['*'] || LANGUAGE_DEFAULT; ++ } catch (error) { /* Oh well. */ } ++ } + } else if (typeof process === 'object') { + _isWindows = (process.platform === 'win32'); + _isMacintosh = (process.platform === 'darwin'); +diff --git a/src/vs/base/node/languagePacks.js b/src/vs/base/node/languagePacks.js +index 2c64061da7..c0ef8faedd 100644 +--- a/src/vs/base/node/languagePacks.js ++++ b/src/vs/base/node/languagePacks.js +@@ -128,7 +128,10 @@ function factory(nodeRequire, path, fs, perf) { + function getLanguagePackConfigurations(userDataPath) { + const configFile = path.join(userDataPath, 'languagepacks.json'); + try { +- return nodeRequire(configFile); ++ // NOTE@coder: Swapped require with readFile since require is cached and ++ // we don't restart the server-side portion of code-server when the ++ // language changes. ++ return JSON.parse(fs.readFileSync(configFile, "utf8")); + } catch (err) { + // Do nothing. If we can't read the file we have no + // language pack config. +diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts +index a599f5a7eb..ec7ccd43f8 100644 +--- a/src/vs/code/browser/workbench/workbench.ts ++++ b/src/vs/code/browser/workbench/workbench.ts +@@ -298,35 +298,6 @@ class WorkspaceProvider implements IWorkspaceProvider { + let workspace: IWorkspace; + let payload = Object.create(null); + +- const query = new URL(document.location.href).searchParams; +- query.forEach((value, key) => { +- switch (key) { +- +- // Folder +- case WorkspaceProvider.QUERY_PARAM_FOLDER: +- workspace = { folderUri: URI.parse(value) }; +- foundWorkspace = true; +- break; +- +- // Workspace +- case WorkspaceProvider.QUERY_PARAM_WORKSPACE: +- workspace = { workspaceUri: URI.parse(value) }; +- foundWorkspace = true; +- break; +- +- // Empty +- case WorkspaceProvider.QUERY_PARAM_EMPTY_WINDOW: +- workspace = undefined; +- foundWorkspace = true; +- break; +- +- // Payload +- case WorkspaceProvider.QUERY_PARAM_PAYLOAD: +- payload = JSON.parse(value); +- break; +- } +- }); +- + // If no workspace is provided through the URL, check for config attribute from server + if (!foundWorkspace) { + if (config.folderUri) { +diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts +index abd1e33b18..bf75952ce1 100644 +--- a/src/vs/platform/environment/common/environment.ts ++++ b/src/vs/platform/environment/common/environment.ts +@@ -37,6 +37,8 @@ export interface ParsedArgs { + logExtensionHostCommunication?: boolean; + 'extensions-dir'?: string; + 'builtin-extensions-dir'?: string; ++ 'extra-extensions-dir'?: string[]; ++ 'extra-builtin-extensions-dir'?: string[]; + extensionDevelopmentPath?: string[]; // // undefined or array of 1 or more local paths or URIs + extensionTestsPath?: string; // either a local path or a URI + 'extension-development-confirm-save'?: boolean; +@@ -147,6 +149,8 @@ export interface IEnvironmentService extends IUserHomeProvider { + disableExtensions: boolean | string[]; + builtinExtensionsPath: string; + extensionsPath?: string; ++ extraExtensionPaths: string[]; ++ extraBuiltinExtensionPaths: string[]; + extensionDevelopmentLocationURI?: URI[]; + extensionTestsLocationURI?: URI; + logExtensionHostCommunication?: boolean; +diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts +index e68e0647c3..49a5aae2fa 100644 +--- a/src/vs/platform/environment/node/argv.ts ++++ b/src/vs/platform/environment/node/argv.ts +@@ -55,6 +55,8 @@ export const OPTIONS: OptionDescriptions> = { + + 'extensions-dir': { type: 'string', deprecates: 'extensionHomePath', cat: 'e', args: 'dir', description: localize('extensionHomePath', "Set the root path for extensions.") }, + 'builtin-extensions-dir': { type: 'string' }, ++ 'extra-builtin-extensions-dir': { type: 'string[]', cat: 'o', description: 'Path to an extra builtin extension directory.' }, ++ 'extra-extensions-dir': { type: 'string[]', cat: 'o', description: 'Path to an extra user extension directory.' }, + 'list-extensions': { type: 'boolean', cat: 'e', description: localize('listExtensions', "List the installed extensions.") }, + 'show-versions': { type: 'boolean', cat: 'e', description: localize('showVersions', "Show versions of installed extensions, when using --list-extension.") }, + 'category': { type: 'string', cat: 'e', description: localize('category', "Filters installed extensions by provided category, when using --list-extension.") }, +@@ -310,4 +312,3 @@ export function buildHelpMessage(productName: string, executableName: string, ve + export function buildVersionMessage(version: string | undefined, commit: string | undefined): string { + return `${version || localize('unknownVersion', "Unknown version")}\n${commit || localize('unknownCommit', "Unknown commit")}\n${process.arch}`; + } +- +diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts +index 0428e1e888..9b3cddcb3a 100644 +--- a/src/vs/platform/environment/node/environmentService.ts ++++ b/src/vs/platform/environment/node/environmentService.ts +@@ -197,6 +197,13 @@ export class EnvironmentService implements IEnvironmentService { + return path.join(this.userHome, product.dataFolderName, 'extensions'); + } + ++ @memoize get extraExtensionPaths(): string[] { ++ return (this._args['extra-extensions-dir'] || []).map((p) => parsePathArg(p, process)); ++ } ++ @memoize get extraBuiltinExtensionPaths(): string[] { ++ return (this._args['extra-builtin-extensions-dir'] || []).map((p) => parsePathArg(p, process)); ++ } ++ + @memoize + get extensionDevelopmentLocationURI(): URI[] | undefined { + const s = this._args.extensionDevelopmentPath; +diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts +index 5b05650591..aa8712d8fb 100644 +--- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts ++++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts +@@ -743,11 +743,15 @@ export class ExtensionManagementService extends Disposable implements IExtension + + private scanSystemExtensions(): Promise { + this.logService.trace('Started scanning system extensions'); +- const systemExtensionsPromise = this.scanExtensions(this.systemExtensionsPath, ExtensionType.System) +- .then(result => { +- this.logService.trace('Scanned system extensions:', result.length); +- return result; +- }); ++ const systemExtensionsPromise = Promise.all([ ++ this.scanExtensions(this.systemExtensionsPath, ExtensionType.System), ++ ...this.environmentService.extraBuiltinExtensionPaths ++ .map((path) => this.scanExtensions(path, ExtensionType.System)) ++ ]).then((results) => { ++ const result = results.reduce((flat, current) => flat.concat(current), []); ++ this.logService.trace('Scanned system extensions:', result.length); ++ return result; ++ }); + if (this.environmentService.isBuilt) { + return systemExtensionsPromise; + } +@@ -769,9 +773,16 @@ export class ExtensionManagementService extends Disposable implements IExtension + .then(([systemExtensions, devSystemExtensions]) => [...systemExtensions, ...devSystemExtensions]); + } + ++ private scanAllUserExtensions(folderName: string, type: ExtensionType): Promise { ++ return Promise.all([ ++ this.scanExtensions(folderName, type), ++ ...this.environmentService.extraExtensionPaths.map((p) => this.scanExtensions(p, ExtensionType.User)) ++ ]).then((results) => results.reduce((flat, current) => flat.concat(current), [])); ++ } ++ + private scanUserExtensions(excludeOutdated: boolean): Promise { + this.logService.trace('Started scanning user extensions'); +- return Promise.all([this.getUninstalledExtensions(), this.scanExtensions(this.extensionsPath, ExtensionType.User)]) ++ return Promise.all([this.getUninstalledExtensions(), this.scanAllUserExtensions(this.extensionsPath, ExtensionType.User)]) + .then(([uninstalled, extensions]) => { + extensions = extensions.filter(e => !uninstalled[new ExtensionIdentifierWithVersion(e.identifier, e.manifest.version).key()]); + if (excludeOutdated) { +@@ -786,6 +797,12 @@ export class ExtensionManagementService extends Disposable implements IExtension + private scanExtensions(root: string, type: ExtensionType): Promise { + const limiter = new Limiter(10); + return pfs.readdir(root) ++ .catch((error) => { ++ if (error.code !== 'ENOENT') { ++ throw error; ++ } ++ return []; ++ }) + .then(extensionsFolders => Promise.all(extensionsFolders.map(extensionFolder => limiter.queue(() => this.scanExtension(extensionFolder, root, type))))) + .then(extensions => extensions.filter(e => e && e.identifier)); + } +@@ -824,7 +841,7 @@ export class ExtensionManagementService extends Disposable implements IExtension + + private async removeUninstalledExtensions(): Promise { + const uninstalled = await this.getUninstalledExtensions(); +- const extensions = await this.scanExtensions(this.extensionsPath, ExtensionType.User); // All user extensions ++ const extensions = await this.scanAllUserExtensions(this.extensionsPath, ExtensionType.User); // All user extensions + const installed: Set = new Set(); + for (const e of extensions) { + if (!uninstalled[new ExtensionIdentifierWithVersion(e.identifier, e.manifest.version).key()]) { +@@ -843,7 +860,7 @@ export class ExtensionManagementService extends Disposable implements IExtension + } + + private removeOutdatedExtensions(): Promise { +- return this.scanExtensions(this.extensionsPath, ExtensionType.User) // All user extensions ++ return this.scanAllUserExtensions(this.extensionsPath, ExtensionType.User) // All user extensions + .then(extensions => { + const toRemove: ILocalExtension[] = []; + +diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts +index 804d113856..30a349f69f 100644 +--- a/src/vs/platform/product/common/product.ts ++++ b/src/vs/platform/product/common/product.ts +@@ -22,11 +22,18 @@ if (isWeb) { + if (Object.keys(product).length === 0) { + assign(product, { + version: '1.41.0-dev', ++ codeServerVersion: 'dev', + nameLong: 'Visual Studio Code Web Dev', + nameShort: 'VSCode Web Dev', + urlProtocol: 'code-oss' + }); + } ++ // NOTE@coder: Add the ability to inject settings from the server. ++ const el = document.getElementById('vscode-remote-product-configuration'); ++ const rawProductConfiguration = el && el.getAttribute('data-settings'); ++ if (rawProductConfiguration) { ++ assign(product, JSON.parse(rawProductConfiguration)); ++ } + } + + // Node: AMD loader +@@ -36,7 +43,7 @@ else if (typeof require !== 'undefined' && typeof require.__$__nodeRequire === ' + const rootPath = path.dirname(getPathFromAmdModule(require, '')); + + product = assign({}, require.__$__nodeRequire(path.join(rootPath, 'product.json')) as IProductConfiguration); +- const pkg = require.__$__nodeRequire(path.join(rootPath, 'package.json')) as { version: string; }; ++ const pkg = require.__$__nodeRequire(path.join(rootPath, 'package.json')) as { version: string; codeServerVersion: string; }; + + // Running out of sources + if (env['VSCODE_DEV']) { +@@ -48,7 +55,8 @@ else if (typeof require !== 'undefined' && typeof require.__$__nodeRequire === ' + } + + assign(product, { +- version: pkg.version ++ version: pkg.version, ++ codeServerVersion: pkg.codeServerVersion, + }); + } + +diff --git a/src/vs/platform/product/common/productService.ts b/src/vs/platform/product/common/productService.ts +index 120fd66644..52547bdb0e 100644 +--- a/src/vs/platform/product/common/productService.ts ++++ b/src/vs/platform/product/common/productService.ts +@@ -16,6 +16,7 @@ export interface IProductService extends Readonly { + + export interface IProductConfiguration { + readonly version: string; ++ readonly codeServerVersion: string; + readonly date?: string; + readonly quality?: string; + readonly commit?: string; +diff --git a/src/vs/platform/remote/browser/browserSocketFactory.ts b/src/vs/platform/remote/browser/browserSocketFactory.ts +index d0f6e6b18a..1966fd297d 100644 +--- a/src/vs/platform/remote/browser/browserSocketFactory.ts ++++ b/src/vs/platform/remote/browser/browserSocketFactory.ts +@@ -205,7 +205,8 @@ export class BrowserSocketFactory implements ISocketFactory { + } + + connect(host: string, port: number, query: string, callback: IConnectCallback): void { +- const socket = this._webSocketFactory.create(`ws://${host}:${port}/?${query}&skipWebSocketFrames=false`); ++ // NOTE@coder: Modified to work against the current path. ++ const socket = this._webSocketFactory.create(`${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${window.location.host}${window.location.pathname}?${query}&skipWebSocketFrames=false`); + const errorListener = socket.onError((err) => callback(err, undefined)); + socket.onOpen(() => { + errorListener.dispose(); +@@ -213,6 +214,3 @@ export class BrowserSocketFactory implements ISocketFactory { + }); + } + } +- +- +- +diff --git a/src/vs/server/browser/client.ts b/src/vs/server/browser/client.ts +new file mode 100644 +index 0000000000..95e70869f2 +--- /dev/null ++++ b/src/vs/server/browser/client.ts +@@ -0,0 +1,224 @@ ++import { Emitter } from 'vs/base/common/event'; ++import { URI } from 'vs/base/common/uri'; ++import { localize } from 'vs/nls'; ++import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; ++import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; ++import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; ++import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; ++import { ILogService } from 'vs/platform/log/common/log'; ++import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; ++import { Registry } from 'vs/platform/registry/common/platform'; ++import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection'; ++import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; ++import { INodeProxyService, NodeProxyChannelClient } from 'vs/server/common/nodeProxy'; ++import { TelemetryChannelClient } from 'vs/server/common/telemetry'; ++import 'vs/workbench/contrib/localizations/browser/localizations.contribution'; ++import { LocalizationsService } from 'vs/workbench/services/localizations/electron-browser/localizationsService'; ++import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; ++ ++class TelemetryService extends TelemetryChannelClient { ++ public constructor( ++ @IRemoteAgentService remoteAgentService: IRemoteAgentService, ++ ) { ++ super(remoteAgentService.getConnection()!.getChannel('telemetry')); ++ } ++} ++ ++const TELEMETRY_SECTION_ID = 'telemetry'; ++ ++Registry.as(Extensions.Configuration).registerConfiguration({ ++ 'id': TELEMETRY_SECTION_ID, ++ 'order': 110, ++ 'type': 'object', ++ 'title': localize('telemetryConfigurationTitle', 'Telemetry'), ++ 'properties': { ++ 'telemetry.enableTelemetry': { ++ 'type': 'boolean', ++ 'description': localize('telemetry.enableTelemetry', 'Enable usage data and errors to be sent to a Microsoft online service.'), ++ 'default': true, ++ 'tags': ['usesOnlineServices'] ++ } ++ } ++}); ++ ++class NodeProxyService extends NodeProxyChannelClient implements INodeProxyService { ++ private readonly _onClose = new Emitter(); ++ public readonly onClose = this._onClose.event; ++ private readonly _onDown = new Emitter(); ++ public readonly onDown = this._onDown.event; ++ private readonly _onUp = new Emitter(); ++ public readonly onUp = this._onUp.event; ++ ++ public constructor( ++ @IRemoteAgentService remoteAgentService: IRemoteAgentService, ++ ) { ++ super(remoteAgentService.getConnection()!.getChannel('nodeProxy')); ++ remoteAgentService.getConnection()!.onDidStateChange((state) => { ++ switch (state.type) { ++ case PersistentConnectionEventType.ConnectionGain: ++ return this._onUp.fire(); ++ case PersistentConnectionEventType.ConnectionLost: ++ return this._onDown.fire(); ++ case PersistentConnectionEventType.ReconnectionPermanentFailure: ++ return this._onClose.fire(); ++ } ++ }); ++ } ++} ++ ++registerSingleton(ILocalizationsService, LocalizationsService); ++registerSingleton(INodeProxyService, NodeProxyService); ++registerSingleton(ITelemetryService, TelemetryService); ++ ++/** ++ * This is called by vs/workbench/browser/web.main.ts after the workbench has ++ * been initialized so we can initialize our own client-side code. ++ */ ++export const initialize = async (services: ServiceCollection): Promise => { ++ const event = new CustomEvent('ide-ready'); ++ window.dispatchEvent(event); ++ ++ if (parent) { ++ // Tell the parent loading has completed. ++ parent.postMessage({ event: 'loaded' }, window.location.origin); ++ ++ // Proxy or stop proxing events as requested by the parent. ++ const listeners = new Map void>(); ++ window.addEventListener('message', (parentEvent) => { ++ const eventName = parentEvent.data.bind || parentEvent.data.unbind; ++ if (eventName) { ++ const oldListener = listeners.get(eventName); ++ if (oldListener) { ++ document.removeEventListener(eventName, oldListener); ++ } ++ } ++ ++ if (parentEvent.data.bind && parentEvent.data.prop) { ++ const listener = (event: Event) => { ++ parent.postMessage({ ++ event: parentEvent.data.event, ++ [parentEvent.data.prop]: event[parentEvent.data.prop as keyof Event] ++ }, window.location.origin); ++ }; ++ listeners.set(parentEvent.data.bind, listener); ++ document.addEventListener(parentEvent.data.bind, listener); ++ } ++ }); ++ } ++ ++ if (!window.isSecureContext) { ++ (services.get(INotificationService) as INotificationService).notify({ ++ severity: Severity.Warning, ++ message: 'code-server is being accessed over an insecure domain. Web views, the clipboard, and other functionality will not work as expected.', ++ actions: { ++ primary: [{ ++ id: 'understand', ++ label: 'I understand', ++ tooltip: '', ++ class: undefined, ++ enabled: true, ++ checked: true, ++ dispose: () => undefined, ++ run: () => { ++ return Promise.resolve(); ++ } ++ }], ++ } ++ }); ++ } ++ ++ const applyUpdate = async (): Promise => { ++ (services.get(ILogService) as ILogService).debug("Applying update..."); ++ ++ const response = await fetch("./update/apply", { ++ headers: { "content-type": "application/json" }, ++ }); ++ if (response.status !== 200) { ++ throw new Error("Unexpected response"); ++ } ++ ++ const json = await response.json(); ++ if (!json.isLatest) { ++ throw new Error("Update failed"); ++ } ++ ++ (services.get(INotificationService) as INotificationService).info(`Updated to ${json.version}`); ++ }; ++ ++ const getUpdate = async (): Promise => { ++ (services.get(ILogService) as ILogService).debug("Checking for update..."); ++ ++ const response = await fetch("./update", { ++ headers: { "content-type": "application/json" }, ++ }); ++ if (response.status !== 200) { ++ throw new Error("unexpected response"); ++ } ++ ++ const json = await response.json(); ++ if (json.isLatest) { ++ return; ++ } ++ ++ (services.get(INotificationService) as INotificationService).notify({ ++ severity: Severity.Info, ++ message: `code-server has an update: ${json.version}`, ++ actions: { ++ primary: [{ ++ id: 'update', ++ label: 'Apply Update', ++ tooltip: '', ++ class: undefined, ++ enabled: true, ++ checked: true, ++ dispose: () => undefined, ++ run: applyUpdate, ++ }], ++ } ++ }); ++ }; ++ ++ const updateLoop = (): void => { ++ getUpdate().catch((error) => { ++ (services.get(ILogService) as ILogService).warn(error); ++ }).finally(() => { ++ setTimeout(updateLoop, 300000); ++ }); ++ }; ++ ++ updateLoop(); ++}; ++ ++export interface Query { ++ [key: string]: string | undefined; ++} ++ ++/** ++ * Split a string up to the delimiter. If the delimiter doesn't exist the first ++ * item will have all the text and the second item will be an empty string. ++ */ ++export const split = (str: string, delimiter: string): [string, string] => { ++ const index = str.indexOf(delimiter); ++ return index !== -1 ? [str.substring(0, index).trim(), str.substring(index + 1)] : [str, '']; ++}; ++ ++/** ++ * Return the URL modified with the specified query variables. It's pretty ++ * stupid so it probably doesn't cover any edge cases. Undefined values will ++ * unset existing values. Doesn't allow duplicates. ++ */ ++export const withQuery = (url: string, replace: Query): string => { ++ const uri = URI.parse(url); ++ const query = { ...replace }; ++ uri.query.split('&').forEach((kv) => { ++ const [key, value] = split(kv, '='); ++ if (!(key in query)) { ++ query[key] = value; ++ } ++ }); ++ return uri.with({ ++ query: Object.keys(query) ++ .filter((k) => typeof query[k] !== 'undefined') ++ .map((k) => `${k}=${query[k]}`).join('&'), ++ }).toString(true); ++}; +diff --git a/src/vs/server/browser/extHostNodeProxy.ts b/src/vs/server/browser/extHostNodeProxy.ts +new file mode 100644 +index 0000000000..ed7c078077 +--- /dev/null ++++ b/src/vs/server/browser/extHostNodeProxy.ts +@@ -0,0 +1,46 @@ ++import { Emitter } from 'vs/base/common/event'; ++import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; ++import { ExtHostNodeProxyShape, MainContext, MainThreadNodeProxyShape } from 'vs/workbench/api/common/extHost.protocol'; ++import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; ++ ++export class ExtHostNodeProxy implements ExtHostNodeProxyShape { ++ _serviceBrand: any; ++ ++ private readonly _onMessage = new Emitter(); ++ public readonly onMessage = this._onMessage.event; ++ private readonly _onClose = new Emitter(); ++ public readonly onClose = this._onClose.event; ++ private readonly _onDown = new Emitter(); ++ public readonly onDown = this._onDown.event; ++ private readonly _onUp = new Emitter(); ++ public readonly onUp = this._onUp.event; ++ ++ private readonly proxy: MainThreadNodeProxyShape; ++ ++ constructor(@IExtHostRpcService rpc: IExtHostRpcService) { ++ this.proxy = rpc.getProxy(MainContext.MainThreadNodeProxy); ++ } ++ ++ public $onMessage(message: string): void { ++ this._onMessage.fire(message); ++ } ++ ++ public $onClose(): void { ++ this._onClose.fire(); ++ } ++ ++ public $onUp(): void { ++ this._onUp.fire(); ++ } ++ ++ public $onDown(): void { ++ this._onDown.fire(); ++ } ++ ++ public send(message: string): void { ++ this.proxy.$send(message); ++ } ++} ++ ++export interface IExtHostNodeProxy extends ExtHostNodeProxy { } ++export const IExtHostNodeProxy = createDecorator('IExtHostNodeProxy'); +diff --git a/src/vs/server/browser/mainThreadNodeProxy.ts b/src/vs/server/browser/mainThreadNodeProxy.ts +new file mode 100644 +index 0000000000..0d2e93edae +--- /dev/null ++++ b/src/vs/server/browser/mainThreadNodeProxy.ts +@@ -0,0 +1,37 @@ ++import { IDisposable } from 'vs/base/common/lifecycle'; ++import { INodeProxyService } from 'vs/server/common/nodeProxy'; ++import { ExtHostContext, IExtHostContext, MainContext, MainThreadNodeProxyShape } from 'vs/workbench/api/common/extHost.protocol'; ++import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; ++ ++@extHostNamedCustomer(MainContext.MainThreadNodeProxy) ++export class MainThreadNodeProxy implements MainThreadNodeProxyShape { ++ private disposed = false; ++ private disposables = []; ++ ++ constructor( ++ extHostContext: IExtHostContext, ++ @INodeProxyService private readonly proxyService: INodeProxyService, ++ ) { ++ if (!extHostContext.remoteAuthority) { // HACK: A terrible way to detect if running in the worker. ++ const proxy = extHostContext.getProxy(ExtHostContext.ExtHostNodeProxy); ++ this.disposables = [ ++ this.proxyService.onMessage((message: string) => proxy.$onMessage(message)), ++ this.proxyService.onClose(() => proxy.$onClose()), ++ this.proxyService.onDown(() => proxy.$onDown()), ++ this.proxyService.onUp(() => proxy.$onUp()), ++ ]; ++ } ++ } ++ ++ $send(message: string): void { ++ if (!this.disposed) { ++ this.proxyService.send(message); ++ } ++ } ++ ++ dispose(): void { ++ this.disposables.forEach((d) => d.dispose()); ++ this.disposables = []; ++ this.disposed = true; ++ } ++} +diff --git a/src/vs/server/browser/worker.ts b/src/vs/server/browser/worker.ts +new file mode 100644 +index 0000000000..0ba93cc070 +--- /dev/null ++++ b/src/vs/server/browser/worker.ts +@@ -0,0 +1,57 @@ ++import { Client } from '@coder/node-browser'; ++import { fromTar } from '@coder/requirefs'; ++import { URI } from 'vs/base/common/uri'; ++import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; ++import { ILogService } from 'vs/platform/log/common/log'; ++import { ExtensionActivationTimesBuilder } from 'vs/workbench/api/common/extHostExtensionActivator'; ++import { IExtHostNodeProxy } from './extHostNodeProxy'; ++ ++export const loadCommonJSModule = async ( ++ module: IExtensionDescription, ++ activationTimesBuilder: ExtensionActivationTimesBuilder, ++ nodeProxy: IExtHostNodeProxy, ++ logService: ILogService, ++ vscode: any, ++): Promise => { ++ const fetchUri = URI.from({ ++ scheme: self.location.protocol.replace(':', ''), ++ authority: self.location.host, ++ path: `${self.location.pathname.replace(/\/static.*\/out\/vs\/workbench\/services\/extensions\/worker\/extensionHostWorkerMain.js$/, '')}/tar`, ++ query: `path=${encodeURIComponent(module.extensionLocation.path)}`, ++ }); ++ const response = await fetch(fetchUri.toString(true)); ++ if (response.status !== 200) { ++ throw new Error(`Failed to download extension "${module.extensionLocation.path}"`); ++ } ++ const client = new Client(nodeProxy, { logger: logService }); ++ const init = await client.handshake(); ++ const buffer = new Uint8Array(await response.arrayBuffer()); ++ const rfs = fromTar(buffer); ++ (self).global = self; ++ rfs.provide('vscode', vscode); ++ Object.keys(client.modules).forEach((key) => { ++ const mod = (client.modules as any)[key]; ++ if (key === 'process') { ++ (self).process = mod; ++ (self).process.env = init.env; ++ return; ++ } ++ ++ rfs.provide(key, mod); ++ switch (key) { ++ case 'buffer': ++ (self).Buffer = mod.Buffer; ++ break; ++ case 'timers': ++ (self).setImmediate = mod.setImmediate; ++ break; ++ } ++ }); ++ ++ try { ++ activationTimesBuilder.codeLoadingStart(); ++ return rfs.require('.'); ++ } finally { ++ activationTimesBuilder.codeLoadingStop(); ++ } ++}; +diff --git a/src/vs/server/common/nodeProxy.ts b/src/vs/server/common/nodeProxy.ts +new file mode 100644 +index 0000000000..14b9de879c +--- /dev/null ++++ b/src/vs/server/common/nodeProxy.ts +@@ -0,0 +1,47 @@ ++import { ReadWriteConnection } from '@coder/node-browser'; ++import { Event } from 'vs/base/common/event'; ++import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; ++import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; ++ ++export const INodeProxyService = createDecorator('nodeProxyService'); ++ ++export interface INodeProxyService extends ReadWriteConnection { ++ _serviceBrand: any; ++ send(message: string): void; ++ onMessage: Event; ++ onUp: Event; ++ onClose: Event; ++ onDown: Event; ++} ++ ++export class NodeProxyChannel implements IServerChannel { ++ constructor(private service: INodeProxyService) {} ++ ++ listen(_: unknown, event: string): Event { ++ switch (event) { ++ case 'onMessage': return this.service.onMessage; ++ } ++ throw new Error(`Invalid listen ${event}`); ++ } ++ ++ async call(_: unknown, command: string, args?: any): Promise { ++ switch (command) { ++ case 'send': return this.service.send(args[0]); ++ } ++ throw new Error(`Invalid call ${command}`); ++ } ++} ++ ++export class NodeProxyChannelClient { ++ _serviceBrand: any; ++ ++ public readonly onMessage: Event; ++ ++ constructor(private readonly channel: IChannel) { ++ this.onMessage = this.channel.listen('onMessage'); ++ } ++ ++ public send(data: string): void { ++ this.channel.call('send', [data]); ++ } ++} +diff --git a/src/vs/server/common/telemetry.ts b/src/vs/server/common/telemetry.ts +new file mode 100644 +index 0000000000..eb62b87798 +--- /dev/null ++++ b/src/vs/server/common/telemetry.ts +@@ -0,0 +1,49 @@ ++import { ITelemetryData } from 'vs/base/common/actions'; ++import { Event } from 'vs/base/common/event'; ++import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; ++import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings'; ++import { ITelemetryInfo, ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; ++ ++export class TelemetryChannel implements IServerChannel { ++ constructor(private service: ITelemetryService) {} ++ ++ listen(_: unknown, event: string): Event { ++ throw new Error(`Invalid listen ${event}`); ++ } ++ ++ call(_: unknown, command: string, args?: any): Promise { ++ switch (command) { ++ case 'publicLog': return this.service.publicLog(args[0], args[1], args[2]); ++ case 'publicLog2': return this.service.publicLog2(args[0], args[1], args[2]); ++ case 'setEnabled': return Promise.resolve(this.service.setEnabled(args[0])); ++ case 'getTelemetryInfo': return this.service.getTelemetryInfo(); ++ } ++ throw new Error(`Invalid call ${command}`); ++ } ++} ++ ++export class TelemetryChannelClient implements ITelemetryService { ++ _serviceBrand: any; ++ ++ constructor(private readonly channel: IChannel) {} ++ ++ public publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): Promise { ++ return this.channel.call('publicLog', [eventName, data, anonymizeFilePaths]); ++ } ++ ++ public publicLog2 = never, T extends GDPRClassification = never>(eventName: string, data?: StrictPropertyCheck, anonymizeFilePaths?: boolean): Promise { ++ return this.channel.call('publicLog2', [eventName, data, anonymizeFilePaths]); ++ } ++ ++ public setEnabled(value: boolean): void { ++ this.channel.call('setEnable', [value]); ++ } ++ ++ public getTelemetryInfo(): Promise { ++ return this.channel.call('getTelemetryInfo'); ++ } ++ ++ public get isOptedIn(): boolean { ++ return true; ++ } ++} +diff --git a/src/vs/server/entry.ts b/src/vs/server/entry.ts +new file mode 100644 +index 0000000000..220a0f4223 +--- /dev/null ++++ b/src/vs/server/entry.ts +@@ -0,0 +1,76 @@ ++import { field } from '@coder/logger'; ++import { setUnexpectedErrorHandler } from 'vs/base/common/errors'; ++import { CodeServerMessage, VscodeMessage } from 'vs/server/ipc'; ++import { logger } from 'vs/server/node/logger'; ++import { enableCustomMarketplace } from 'vs/server/node/marketplace'; ++import { Vscode } from 'vs/server/node/server'; ++ ++setUnexpectedErrorHandler((error) => logger.warn(error instanceof Error ? error.message : error)); ++enableCustomMarketplace(); ++ ++/** ++ * Ensure we control when the process exits. ++ */ ++const exit = process.exit; ++process.exit = function(code?: number) { ++ logger.warn(`process.exit() was prevented: ${code || 'unknown code'}.`); ++} as (code?: number) => never; ++ ++// Kill VS Code if the parent process dies. ++if (typeof process.env.CODE_SERVER_PARENT_PID !== 'undefined') { ++ const parentPid = parseInt(process.env.CODE_SERVER_PARENT_PID, 10); ++ setInterval(() => { ++ try { ++ process.kill(parentPid, 0); // Throws an exception if the process doesn't exist anymore. ++ } catch (e) { ++ exit(); ++ } ++ }, 5000); ++} else { ++ logger.error('no parent process'); ++ exit(1); ++} ++ ++const vscode = new Vscode(); ++const send = (message: VscodeMessage): void => { ++ if (!process.send) { ++ throw new Error('not spawned with IPC'); ++ } ++ process.send(message); ++}; ++ ++// Wait for the init message then start up VS Code. Subsequent messages will ++// return new workbench options without starting a new instance. ++process.on('message', async (message: CodeServerMessage, socket) => { ++ logger.debug('got message from code-server', field('message', message)); ++ switch (message.type) { ++ case 'init': ++ try { ++ const options = await vscode.initialize(message.options); ++ send({ type: 'options', id: message.id, options }); ++ } catch (error) { ++ logger.error(error.message); ++ exit(1); ++ } ++ break; ++ case 'cli': ++ try { ++ await vscode.cli(message.args); ++ exit(0); ++ } catch (error) { ++ logger.error(error.message); ++ exit(1); ++ } ++ break; ++ case 'socket': ++ vscode.handleWebSocket(socket, message.query); ++ break; ++ } ++}); ++if (!process.send) { ++ logger.error('not spawned with IPC'); ++ exit(1); ++} else { ++ // This lets the parent know the child is ready to receive messages. ++ send({ type: 'ready' }); ++} +diff --git a/src/vs/server/fork.js b/src/vs/server/fork.js +new file mode 100644 +index 0000000000..56331ff1fc +--- /dev/null ++++ b/src/vs/server/fork.js +@@ -0,0 +1,3 @@ ++// This must be a JS file otherwise when it gets compiled it turns into AMD ++// syntax which will not work without the right loader. ++require('../../bootstrap-amd').load('vs/server/entry'); +diff --git a/src/vs/server/ipc.d.ts b/src/vs/server/ipc.d.ts +new file mode 100644 +index 0000000000..a0d1d0df54 +--- /dev/null ++++ b/src/vs/server/ipc.d.ts +@@ -0,0 +1,108 @@ ++/** ++ * External interfaces for integration into code-server over IPC. No vs imports ++ * should be made in this file. ++ */ ++ ++export interface InitMessage { ++ type: 'init'; ++ id: string; ++ options: VscodeOptions; ++} ++ ++export type Query = { [key: string]: string | string[] | undefined }; ++ ++export interface SocketMessage { ++ type: 'socket'; ++ query: Query; ++} ++ ++export interface CliMessage { ++ type: 'cli'; ++ args: Args; ++} ++ ++export type CodeServerMessage = InitMessage | SocketMessage | CliMessage; ++ ++export interface ReadyMessage { ++ type: 'ready'; ++} ++ ++export interface OptionsMessage { ++ id: string; ++ type: 'options'; ++ options: WorkbenchOptions; ++} ++ ++export type VscodeMessage = ReadyMessage | OptionsMessage; ++ ++export interface StartPath { ++ url: string; ++ workspace: boolean; ++} ++ ++export interface Args { ++ 'user-data-dir'?: string; ++ ++ 'extensions-dir'?: string; ++ 'builtin-extensions-dir'?: string; ++ 'extra-extensions-dir'?: string[]; ++ 'extra-builtin-extensions-dir'?: string[]; ++ ++ locale?: string ++ ++ log?: string; ++ verbose?: boolean; ++ ++ _: string[]; ++} ++ ++export interface VscodeOptions { ++ readonly args: Args; ++ readonly remoteAuthority: string; ++ readonly startPath?: StartPath; ++} ++ ++export interface VscodeOptionsMessage extends VscodeOptions { ++ readonly id: string; ++} ++ ++export interface UriComponents { ++ readonly scheme: string; ++ readonly authority: string; ++ readonly path: string; ++ readonly query: string; ++ readonly fragment: string; ++} ++ ++export interface NLSConfiguration { ++ locale: string; ++ availableLanguages: { ++ [key: string]: string; ++ }; ++ pseudo?: boolean; ++ _languagePackSupport?: boolean; ++} ++ ++export interface WorkbenchOptions { ++ readonly workbenchWebConfiguration: { ++ readonly remoteAuthority?: string; ++ readonly folderUri?: UriComponents; ++ readonly workspaceUri?: UriComponents; ++ readonly logLevel?: number; ++ }; ++ readonly remoteUserDataUri: UriComponents; ++ readonly productConfiguration: { ++ readonly extensionsGallery?: { ++ readonly serviceUrl: string; ++ readonly itemUrl: string; ++ readonly controlUrl: string; ++ readonly recommendationsUrl: string; ++ }; ++ }; ++ readonly nlsConfiguration: NLSConfiguration; ++ readonly commit: string; ++} ++ ++export interface WorkbenchOptionsMessage { ++ id: string; ++} +diff --git a/src/vs/server/node/channel.ts b/src/vs/server/node/channel.ts +new file mode 100644 +index 0000000000..9c240b992d +--- /dev/null ++++ b/src/vs/server/node/channel.ts +@@ -0,0 +1,343 @@ ++import { Server } from '@coder/node-browser'; ++import * as path from 'path'; ++import { VSBuffer, VSBufferReadableStream } from 'vs/base/common/buffer'; ++import { Emitter, Event } from 'vs/base/common/event'; ++import { IDisposable } from 'vs/base/common/lifecycle'; ++import { OS } from 'vs/base/common/platform'; ++import { ReadableStreamEventPayload } from 'vs/base/common/stream'; ++import { URI, UriComponents } from 'vs/base/common/uri'; ++import { transformOutgoingURIs } from 'vs/base/common/uriIpc'; ++import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; ++import { IDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnostics'; ++import { IEnvironmentService } from 'vs/platform/environment/common/environment'; ++import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; ++import { FileDeleteOptions, FileOpenOptions, FileOverwriteOptions, FileReadStreamOptions, FileType, FileWriteOptions, IStat, IWatchOptions } from 'vs/platform/files/common/files'; ++import { createReadStream } from 'vs/platform/files/common/io'; ++import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; ++import { ILogService } from 'vs/platform/log/common/log'; ++import product from 'vs/platform/product/common/product'; ++import { IRemoteAgentEnvironment, RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment'; ++import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; ++import { INodeProxyService } from 'vs/server/common/nodeProxy'; ++import { getTranslations } from 'vs/server/node/nls'; ++import { getUriTransformer } from 'vs/server/node/util'; ++import { IFileChangeDto } from 'vs/workbench/api/common/extHost.protocol'; ++import { ExtensionScanner, ExtensionScannerInput } from 'vs/workbench/services/extensions/node/extensionPoints'; ++ ++/** ++ * Extend the file provider to allow unwatching. ++ */ ++class Watcher extends DiskFileSystemProvider { ++ public readonly watches = new Map(); ++ ++ public dispose(): void { ++ this.watches.forEach((w) => w.dispose()); ++ this.watches.clear(); ++ super.dispose(); ++ } ++ ++ public _watch(req: number, resource: URI, opts: IWatchOptions): void { ++ this.watches.set(req, this.watch(resource, opts)); ++ } ++ ++ public unwatch(req: number): void { ++ this.watches.get(req)!.dispose(); ++ this.watches.delete(req); ++ } ++} ++ ++export class FileProviderChannel implements IServerChannel, IDisposable { ++ private readonly provider: DiskFileSystemProvider; ++ private readonly watchers = new Map(); ++ ++ public constructor( ++ private readonly environmentService: IEnvironmentService, ++ private readonly logService: ILogService, ++ ) { ++ this.provider = new DiskFileSystemProvider(this.logService); ++ } ++ ++ public listen(context: RemoteAgentConnectionContext, event: string, args?: any): Event { ++ switch (event) { ++ case 'filechange': return this.filechange(context, args[0]); ++ case 'readFileStream': return this.readFileStream(args[0], args[1]); ++ } ++ ++ throw new Error(`Invalid listen '${event}'`); ++ } ++ ++ private filechange(context: RemoteAgentConnectionContext, session: string): Event { ++ const emitter = new Emitter({ ++ onFirstListenerAdd: () => { ++ const provider = new Watcher(this.logService); ++ this.watchers.set(session, provider); ++ const transformer = getUriTransformer(context.remoteAuthority); ++ provider.onDidChangeFile((events) => { ++ emitter.fire(events.map((event) => ({ ++ ...event, ++ resource: transformer.transformOutgoing(event.resource), ++ }))); ++ }); ++ provider.onDidErrorOccur((event) => this.logService.error(event)); ++ }, ++ onLastListenerRemove: () => { ++ this.watchers.get(session)!.dispose(); ++ this.watchers.delete(session); ++ }, ++ }); ++ ++ return emitter.event; ++ } ++ ++ private readFileStream(resource: UriComponents, opts: FileReadStreamOptions): Event> { ++ let fileStream: VSBufferReadableStream | undefined; ++ const emitter = new Emitter>({ ++ onFirstListenerAdd: () => { ++ if (!fileStream) { ++ fileStream = createReadStream(this.provider, this.transform(resource), { ++ ...opts, ++ bufferSize: 64 * 1024, // From DiskFileSystemProvider ++ }); ++ fileStream.on('data', (data) => emitter.fire(data)); ++ fileStream.on('error', (error) => emitter.fire(error)); ++ fileStream.on('end', () => emitter.fire('end')); ++ } ++ }, ++ onLastListenerRemove: () => fileStream && fileStream.destroy(), ++ }); ++ ++ return emitter.event; ++ } ++ ++ public call(_: unknown, command: string, args?: any): Promise { ++ switch (command) { ++ case 'stat': return this.stat(args[0]); ++ case 'open': return this.open(args[0], args[1]); ++ case 'close': return this.close(args[0]); ++ case 'read': return this.read(args[0], args[1], args[2]); ++ case 'readFile': return this.readFile(args[0]); ++ case 'write': return this.write(args[0], args[1], args[2], args[3], args[4]); ++ case 'writeFile': return this.writeFile(args[0], args[1], args[2]); ++ case 'delete': return this.delete(args[0], args[1]); ++ case 'mkdir': return this.mkdir(args[0]); ++ case 'readdir': return this.readdir(args[0]); ++ case 'rename': return this.rename(args[0], args[1], args[2]); ++ case 'copy': return this.copy(args[0], args[1], args[2]); ++ case 'watch': return this.watch(args[0], args[1], args[2], args[3]); ++ case 'unwatch': return this.unwatch(args[0], args[1]); ++ } ++ ++ throw new Error(`Invalid call '${command}'`); ++ } ++ ++ public dispose(): void { ++ this.watchers.forEach((w) => w.dispose()); ++ this.watchers.clear(); ++ } ++ ++ private async stat(resource: UriComponents): Promise { ++ return this.provider.stat(this.transform(resource)); ++ } ++ ++ private async open(resource: UriComponents, opts: FileOpenOptions): Promise { ++ return this.provider.open(this.transform(resource), opts); ++ } ++ ++ private async close(fd: number): Promise { ++ return this.provider.close(fd); ++ } ++ ++ private async read(fd: number, pos: number, length: number): Promise<[VSBuffer, number]> { ++ const buffer = VSBuffer.alloc(length); ++ const bytesRead = await this.provider.read(fd, pos, buffer.buffer, 0, length); ++ return [buffer, bytesRead]; ++ } ++ ++ private async readFile(resource: UriComponents): Promise { ++ return VSBuffer.wrap(await this.provider.readFile(this.transform(resource))); ++ } ++ ++ private write(fd: number, pos: number, buffer: VSBuffer, offset: number, length: number): Promise { ++ return this.provider.write(fd, pos, buffer.buffer, offset, length); ++ } ++ ++ private writeFile(resource: UriComponents, buffer: VSBuffer, opts: FileWriteOptions): Promise { ++ return this.provider.writeFile(this.transform(resource), buffer.buffer, opts); ++ } ++ ++ private async delete(resource: UriComponents, opts: FileDeleteOptions): Promise { ++ return this.provider.delete(this.transform(resource), opts); ++ } ++ ++ private async mkdir(resource: UriComponents): Promise { ++ return this.provider.mkdir(this.transform(resource)); ++ } ++ ++ private async readdir(resource: UriComponents): Promise<[string, FileType][]> { ++ return this.provider.readdir(this.transform(resource)); ++ } ++ ++ private async rename(resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): Promise { ++ return this.provider.rename(this.transform(resource), URI.from(target), opts); ++ } ++ ++ private copy(resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): Promise { ++ return this.provider.copy(this.transform(resource), URI.from(target), opts); ++ } ++ ++ private async watch(session: string, req: number, resource: UriComponents, opts: IWatchOptions): Promise { ++ this.watchers.get(session)!._watch(req, this.transform(resource), opts); ++ } ++ ++ private async unwatch(session: string, req: number): Promise { ++ this.watchers.get(session)!.unwatch(req); ++ } ++ ++ private transform(resource: UriComponents): URI { ++ // Used for walkthrough content. ++ if (/^\/static[^/]*\//.test(resource.path)) { ++ return URI.file(this.environmentService.appRoot + resource.path.replace(/^\/static[^/]*\//, '/')); ++ // Used by the webview service worker to load resources. ++ } else if (resource.path === '/vscode-resource' && resource.query) { ++ try { ++ const query = JSON.parse(resource.query); ++ if (query.requestResourcePath) { ++ return URI.file(query.requestResourcePath); ++ } ++ } catch (error) { /* Carry on. */ } ++ } ++ return URI.from(resource); ++ } ++} ++ ++export class ExtensionEnvironmentChannel implements IServerChannel { ++ public constructor( ++ private readonly environment: IEnvironmentService, ++ private readonly log: ILogService, ++ private readonly telemetry: ITelemetryService, ++ private readonly connectionToken: string, ++ ) {} ++ ++ public listen(_: unknown, event: string): Event { ++ throw new Error(`Invalid listen '${event}'`); ++ } ++ ++ public async call(context: any, command: string, args?: any): Promise { ++ switch (command) { ++ case 'getEnvironmentData': ++ return transformOutgoingURIs( ++ await this.getEnvironmentData(args.language), ++ getUriTransformer(context.remoteAuthority), ++ ); ++ case 'getDiagnosticInfo': return this.getDiagnosticInfo(); ++ case 'disableTelemetry': return this.disableTelemetry(); ++ } ++ throw new Error(`Invalid call '${command}'`); ++ } ++ ++ private async getEnvironmentData(locale: string): Promise { ++ return { ++ pid: process.pid, ++ connectionToken: this.connectionToken, ++ appRoot: URI.file(this.environment.appRoot), ++ appSettingsHome: this.environment.appSettingsHome, ++ settingsPath: this.environment.machineSettingsHome, ++ logsPath: URI.file(this.environment.logsPath), ++ extensionsPath: URI.file(this.environment.extensionsPath!), ++ extensionHostLogsPath: URI.file(path.join(this.environment.logsPath, 'extension-host')), ++ globalStorageHome: URI.file(this.environment.globalStorageHome), ++ userHome: URI.file(this.environment.userHome), ++ extensions: await this.scanExtensions(locale), ++ os: OS, ++ }; ++ } ++ ++ private async scanExtensions(locale: string): Promise { ++ const translations = await getTranslations(locale, this.environment.userDataPath); ++ ++ const scanMultiple = (isBuiltin: boolean, isUnderDevelopment: boolean, paths: string[]): Promise => { ++ return Promise.all(paths.map((path) => { ++ return ExtensionScanner.scanExtensions(new ExtensionScannerInput( ++ product.version, ++ product.commit, ++ locale, ++ !!process.env.VSCODE_DEV, ++ path, ++ isBuiltin, ++ isUnderDevelopment, ++ translations, ++ ), this.log); ++ })); ++ }; ++ ++ const scanBuiltin = async (): Promise => { ++ return scanMultiple(true, false, [this.environment.builtinExtensionsPath, ...this.environment.extraBuiltinExtensionPaths]); ++ }; ++ ++ const scanInstalled = async (): Promise => { ++ return scanMultiple(false, true, [this.environment.extensionsPath!, ...this.environment.extraExtensionPaths]); ++ }; ++ ++ return Promise.all([scanBuiltin(), scanInstalled()]).then((allExtensions) => { ++ const uniqueExtensions = new Map(); ++ allExtensions.forEach((multipleExtensions) => { ++ multipleExtensions.forEach((extensions) => { ++ extensions.forEach((extension) => { ++ const id = ExtensionIdentifier.toKey(extension.identifier); ++ if (uniqueExtensions.has(id)) { ++ const oldPath = uniqueExtensions.get(id)!.extensionLocation.fsPath; ++ const newPath = extension.extensionLocation.fsPath; ++ this.log.warn(`${oldPath} has been overridden ${newPath}`); ++ } ++ uniqueExtensions.set(id, extension); ++ }); ++ }); ++ }); ++ return Array.from(uniqueExtensions.values()); ++ }); ++ } ++ ++ private getDiagnosticInfo(): Promise { ++ throw new Error('not implemented'); ++ } ++ ++ private async disableTelemetry(): Promise { ++ this.telemetry.setEnabled(false); ++ } ++} ++ ++export class NodeProxyService implements INodeProxyService { ++ public _serviceBrand = undefined; ++ ++ public readonly server: Server; ++ ++ private readonly _onMessage = new Emitter(); ++ public readonly onMessage = this._onMessage.event; ++ private readonly _$onMessage = new Emitter(); ++ public readonly $onMessage = this._$onMessage.event; ++ public readonly _onDown = new Emitter(); ++ public readonly onDown = this._onDown.event; ++ public readonly _onUp = new Emitter(); ++ public readonly onUp = this._onUp.event; ++ ++ // Unused because the server connection will never permanently close. ++ private readonly _onClose = new Emitter(); ++ public readonly onClose = this._onClose.event; ++ ++ public constructor() { ++ // TODO: down/up ++ this.server = new Server({ ++ onMessage: this.$onMessage, ++ onClose: this.onClose, ++ onDown: this.onDown, ++ onUp: this.onUp, ++ send: (message: string): void => { ++ this._onMessage.fire(message); ++ } ++ }); ++ } ++ ++ public send(message: string): void { ++ this._$onMessage.fire(message); ++ } ++} +diff --git a/src/vs/server/node/connection.ts b/src/vs/server/node/connection.ts +new file mode 100644 +index 0000000000..9b8969690c +--- /dev/null ++++ b/src/vs/server/node/connection.ts +@@ -0,0 +1,158 @@ ++import * as cp from 'child_process'; ++import { getPathFromAmdModule } from 'vs/base/common/amd'; ++import { VSBuffer } from 'vs/base/common/buffer'; ++import { Emitter } from 'vs/base/common/event'; ++import { ISocket } from 'vs/base/parts/ipc/common/ipc.net'; ++import { NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; ++import { IEnvironmentService } from 'vs/platform/environment/common/environment'; ++import { ILogService } from 'vs/platform/log/common/log'; ++import { getNlsConfiguration } from 'vs/server/node/nls'; ++import { Protocol } from 'vs/server/node/protocol'; ++import { uriTransformerPath } from 'vs/server/node/util'; ++import { IExtHostReadyMessage } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; ++ ++export abstract class Connection { ++ private readonly _onClose = new Emitter(); ++ public readonly onClose = this._onClose.event; ++ private disposed = false; ++ private _offline: number | undefined; ++ ++ public constructor(protected protocol: Protocol, public readonly token: string) {} ++ ++ public get offline(): number | undefined { ++ return this._offline; ++ } ++ ++ public reconnect(socket: ISocket, buffer: VSBuffer): void { ++ this._offline = undefined; ++ this.doReconnect(socket, buffer); ++ } ++ ++ public dispose(): void { ++ if (!this.disposed) { ++ this.disposed = true; ++ this.doDispose(); ++ this._onClose.fire(); ++ } ++ } ++ ++ protected setOffline(): void { ++ if (!this._offline) { ++ this._offline = Date.now(); ++ } ++ } ++ ++ /** ++ * Set up the connection on a new socket. ++ */ ++ protected abstract doReconnect(socket: ISocket, buffer: VSBuffer): void; ++ protected abstract doDispose(): void; ++} ++ ++/** ++ * Used for all the IPC channels. ++ */ ++export class ManagementConnection extends Connection { ++ public constructor(protected protocol: Protocol, token: string) { ++ super(protocol, token); ++ protocol.onClose(() => this.dispose()); // Explicit close. ++ protocol.onSocketClose(() => this.setOffline()); // Might reconnect. ++ } ++ ++ protected doDispose(): void { ++ this.protocol.sendDisconnect(); ++ this.protocol.dispose(); ++ this.protocol.getSocket().end(); ++ } ++ ++ protected doReconnect(socket: ISocket, buffer: VSBuffer): void { ++ this.protocol.beginAcceptReconnection(socket, buffer); ++ this.protocol.endAcceptReconnection(); ++ } ++} ++ ++export class ExtensionHostConnection extends Connection { ++ private process?: cp.ChildProcess; ++ ++ public constructor( ++ locale:string, protocol: Protocol, buffer: VSBuffer, token: string, ++ private readonly log: ILogService, ++ private readonly environment: IEnvironmentService, ++ ) { ++ super(protocol, token); ++ this.protocol.dispose(); ++ this.spawn(locale, buffer).then((p) => this.process = p); ++ this.protocol.getUnderlyingSocket().pause(); ++ } ++ ++ protected doDispose(): void { ++ if (this.process) { ++ this.process.kill(); ++ } ++ this.protocol.getSocket().end(); ++ } ++ ++ protected doReconnect(socket: ISocket, buffer: VSBuffer): void { ++ // This is just to set the new socket. ++ this.protocol.beginAcceptReconnection(socket, null); ++ this.protocol.dispose(); ++ this.sendInitMessage(buffer); ++ } ++ ++ private sendInitMessage(buffer: VSBuffer): void { ++ const socket = this.protocol.getUnderlyingSocket(); ++ socket.pause(); ++ this.process!.send({ // Process must be set at this point. ++ type: 'VSCODE_EXTHOST_IPC_SOCKET', ++ initialDataChunk: (buffer.buffer as Buffer).toString('base64'), ++ skipWebSocketFrames: this.protocol.getSocket() instanceof NodeSocket, ++ }, socket); ++ } ++ ++ private async spawn(locale: string, buffer: VSBuffer): Promise { ++ const config = await getNlsConfiguration(locale, this.environment.userDataPath); ++ const proc = cp.fork( ++ getPathFromAmdModule(require, 'bootstrap-fork'), ++ [ '--type=extensionHost', `--uriTransformerPath=${uriTransformerPath}` ], ++ { ++ env: { ++ ...process.env, ++ AMD_ENTRYPOINT: 'vs/workbench/services/extensions/node/extensionHostProcess', ++ PIPE_LOGGING: 'true', ++ VERBOSE_LOGGING: 'true', ++ VSCODE_EXTHOST_WILL_SEND_SOCKET: 'true', ++ VSCODE_HANDLES_UNCAUGHT_ERRORS: 'true', ++ VSCODE_LOG_STACK: 'false', ++ VSCODE_LOG_LEVEL: this.environment.verbose ? 'trace' : this.environment.log, ++ VSCODE_NLS_CONFIG: JSON.stringify(config), ++ }, ++ silent: true, ++ }, ++ ); ++ ++ proc.on('error', () => this.dispose()); ++ proc.on('exit', () => this.dispose()); ++ if (proc.stdout && proc.stderr) { ++ proc.stdout.setEncoding('utf8').on('data', (d) => this.log.info('Extension host stdout', d)); ++ proc.stderr.setEncoding('utf8').on('data', (d) => this.log.error('Extension host stderr', d)); ++ } ++ proc.on('message', (event) => { ++ if (event && event.type === '__$console') { ++ const severity = (this.log)[event.severity] ? event.severity : 'info'; ++ (this.log)[severity]('Extension host', event.arguments); ++ } ++ if (event && event.type === 'VSCODE_EXTHOST_DISCONNECTED') { ++ this.setOffline(); ++ } ++ }); ++ ++ const listen = (message: IExtHostReadyMessage) => { ++ if (message.type === 'VSCODE_EXTHOST_IPC_READY') { ++ proc.removeListener('message', listen); ++ this.sendInitMessage(buffer); ++ } ++ }; ++ ++ return proc.on('message', listen); ++ } ++} +diff --git a/src/vs/server/node/insights.ts b/src/vs/server/node/insights.ts +new file mode 100644 +index 0000000000..a0ece345f2 +--- /dev/null ++++ b/src/vs/server/node/insights.ts +@@ -0,0 +1,124 @@ ++import * as appInsights from 'applicationinsights'; ++import * as https from 'https'; ++import * as http from 'http'; ++import * as os from 'os'; ++ ++class Channel { ++ public get _sender() { ++ throw new Error('unimplemented'); ++ } ++ public get _buffer() { ++ throw new Error('unimplemented'); ++ } ++ ++ public setUseDiskRetryCaching(): void { ++ throw new Error('unimplemented'); ++ } ++ public send(): void { ++ throw new Error('unimplemented'); ++ } ++ public triggerSend(): void { ++ throw new Error('unimplemented'); ++ } ++} ++ ++export class TelemetryClient { ++ public context: any = undefined; ++ public commonProperties: any = undefined; ++ public config: any = {}; ++ ++ public channel: any = new Channel(); ++ ++ public addTelemetryProcessor(): void { ++ throw new Error('unimplemented'); ++ } ++ ++ public clearTelemetryProcessors(): void { ++ throw new Error('unimplemented'); ++ } ++ ++ public runTelemetryProcessors(): void { ++ throw new Error('unimplemented'); ++ } ++ ++ public trackTrace(): void { ++ throw new Error('unimplemented'); ++ } ++ ++ public trackMetric(): void { ++ throw new Error('unimplemented'); ++ } ++ ++ public trackException(): void { ++ throw new Error('unimplemented'); ++ } ++ ++ public trackRequest(): void { ++ throw new Error('unimplemented'); ++ } ++ ++ public trackDependency(): void { ++ throw new Error('unimplemented'); ++ } ++ ++ public track(): void { ++ throw new Error('unimplemented'); ++ } ++ ++ public trackNodeHttpRequestSync(): void { ++ throw new Error('unimplemented'); ++ } ++ ++ public trackNodeHttpRequest(): void { ++ throw new Error('unimplemented'); ++ } ++ ++ public trackNodeHttpDependency(): void { ++ throw new Error('unimplemented'); ++ } ++ ++ public trackEvent(options: appInsights.Contracts.EventTelemetry): void { ++ if (!options.properties) { ++ options.properties = {}; ++ } ++ if (!options.measurements) { ++ options.measurements = {}; ++ } ++ ++ try { ++ const cpus = os.cpus(); ++ options.measurements.cores = cpus.length; ++ options.properties['common.cpuModel'] = cpus[0].model; ++ } catch (error) {} ++ ++ try { ++ options.measurements.memoryFree = os.freemem(); ++ options.measurements.memoryTotal = os.totalmem(); ++ } catch (error) {} ++ ++ try { ++ options.properties['common.shell'] = os.userInfo().shell; ++ options.properties['common.release'] = os.release(); ++ options.properties['common.arch'] = os.arch(); ++ } catch (error) {} ++ ++ try { ++ const url = process.env.TELEMETRY_URL || 'https://v1.telemetry.coder.com/track'; ++ const request = (/^http:/.test(url) ? http : https).request(url, { ++ method: 'POST', ++ headers: { ++ 'Content-Type': 'application/json', ++ }, ++ }); ++ request.on('error', () => { /* We don't care. */ }); ++ request.write(JSON.stringify(options)); ++ request.end(); ++ } catch (error) {} ++ } ++ ++ public flush(options: { callback: (v: string) => void }): void { ++ if (options.callback) { ++ options.callback(''); ++ } ++ } ++} +diff --git a/src/vs/server/node/ipc.ts b/src/vs/server/node/ipc.ts +new file mode 100644 +index 0000000000..5e560eb46e +--- /dev/null ++++ b/src/vs/server/node/ipc.ts +@@ -0,0 +1,61 @@ ++import * as cp from 'child_process'; ++import { Emitter } from 'vs/base/common/event'; ++ ++enum ControlMessage { ++ okToChild = 'ok>', ++ okFromChild = 'ok<', ++} ++ ++interface RelaunchMessage { ++ type: 'relaunch'; ++ version: string; ++} ++ ++export type Message = RelaunchMessage; ++ ++class IpcMain { ++ protected readonly _onMessage = new Emitter(); ++ public readonly onMessage = this._onMessage.event; ++ ++ public handshake(child?: cp.ChildProcess): Promise { ++ return new Promise((resolve, reject) => { ++ const target = child || process; ++ if (!target.send) { ++ throw new Error('Not spawned with IPC enabled'); ++ } ++ target.on('message', (message) => { ++ if (message === child ? ControlMessage.okFromChild : ControlMessage.okToChild) { ++ target.removeAllListeners(); ++ target.on('message', (msg) => this._onMessage.fire(msg)); ++ if (child) { ++ target.send!(ControlMessage.okToChild); ++ } ++ resolve(); ++ } ++ }); ++ if (child) { ++ child.once('error', reject); ++ child.once('exit', (code) => { ++ const error = new Error(`Unexpected exit with code ${code}`); ++ (error as any).code = code; ++ reject(error); ++ }); ++ } else { ++ target.send(ControlMessage.okFromChild); ++ } ++ }); ++ } ++ ++ public relaunch(version: string): void { ++ this.send({ type: 'relaunch', version }); ++ } ++ ++ private send(message: Message): void { ++ if (!process.send) { ++ throw new Error('Not a child process with IPC enabled'); ++ } ++ process.send(message); ++ } ++} ++ ++export const ipcMain = new IpcMain(); +diff --git a/src/vs/server/node/logger.ts b/src/vs/server/node/logger.ts +new file mode 100644 +index 0000000000..2a39c524aa +--- /dev/null ++++ b/src/vs/server/node/logger.ts +@@ -0,0 +1,2 @@ ++import { logger as baseLogger } from '@coder/logger'; ++export const logger = baseLogger.named('vscode'); +diff --git a/src/vs/server/node/marketplace.ts b/src/vs/server/node/marketplace.ts +new file mode 100644 +index 0000000000..8956fc40d4 +--- /dev/null ++++ b/src/vs/server/node/marketplace.ts +@@ -0,0 +1,174 @@ ++import * as fs from 'fs'; ++import * as path from 'path'; ++import * as tarStream from 'tar-stream'; ++import * as util from 'util'; ++import { CancellationToken } from 'vs/base/common/cancellation'; ++import { mkdirp } from 'vs/base/node/pfs'; ++import * as vszip from 'vs/base/node/zip'; ++import * as nls from 'vs/nls'; ++import product from 'vs/platform/product/common/product'; ++ ++// We will be overriding these, so keep a reference to the original. ++const vszipExtract = vszip.extract; ++const vszipBuffer = vszip.buffer; ++ ++export interface IExtractOptions { ++ overwrite?: boolean; ++ /** ++ * Source path within the TAR/ZIP archive. Only the files ++ * contained in this path will be extracted. ++ */ ++ sourcePath?: string; ++} ++ ++export interface IFile { ++ path: string; ++ contents?: Buffer | string; ++ localPath?: string; ++} ++ ++export const tar = async (tarPath: string, files: IFile[]): Promise => { ++ const pack = tarStream.pack(); ++ const chunks: Buffer[] = []; ++ const ended = new Promise((resolve) => { ++ pack.on('end', () => resolve(Buffer.concat(chunks))); ++ }); ++ pack.on('data', (chunk: Buffer) => chunks.push(chunk)); ++ for (let i = 0; i < files.length; i++) { ++ const file = files[i]; ++ pack.entry({ name: file.path }, file.contents); ++ } ++ pack.finalize(); ++ await util.promisify(fs.writeFile)(tarPath, await ended); ++ return tarPath; ++}; ++ ++export const extract = async (archivePath: string, extractPath: string, options: IExtractOptions = {}, token: CancellationToken): Promise => { ++ try { ++ await extractTar(archivePath, extractPath, options, token); ++ } catch (error) { ++ if (error.toString().includes('Invalid tar header')) { ++ await vszipExtract(archivePath, extractPath, options, token); ++ } ++ } ++}; ++ ++export const buffer = (targetPath: string, filePath: string): Promise => { ++ return new Promise(async (resolve, reject) => { ++ try { ++ let done: boolean = false; ++ await extractAssets(targetPath, new RegExp(filePath), (assetPath: string, data: Buffer) => { ++ if (path.normalize(assetPath) === path.normalize(filePath)) { ++ done = true; ++ resolve(data); ++ } ++ }); ++ if (!done) { ++ throw new Error('couldn\'t find asset ' + filePath); ++ } ++ } catch (error) { ++ if (error.toString().includes('Invalid tar header')) { ++ vszipBuffer(targetPath, filePath).then(resolve).catch(reject); ++ } else { ++ reject(error); ++ } ++ } ++ }); ++}; ++ ++const extractAssets = async (tarPath: string, match: RegExp, callback: (path: string, data: Buffer) => void): Promise => { ++ return new Promise((resolve, reject): void => { ++ const extractor = tarStream.extract(); ++ const fail = (error: Error) => { ++ extractor.destroy(); ++ reject(error); ++ }; ++ extractor.once('error', fail); ++ extractor.on('entry', async (header, stream, next) => { ++ const name = header.name; ++ if (match.test(name)) { ++ extractData(stream).then((data) => { ++ callback(name, data); ++ next(); ++ }).catch(fail); ++ } else { ++ stream.on('end', () => next()); ++ stream.resume(); // Just drain it. ++ } ++ }); ++ extractor.on('finish', resolve); ++ fs.createReadStream(tarPath).pipe(extractor); ++ }); ++}; ++ ++const extractData = (stream: NodeJS.ReadableStream): Promise => { ++ return new Promise((resolve, reject): void => { ++ const fileData: Buffer[] = []; ++ stream.on('error', reject); ++ stream.on('end', () => resolve(Buffer.concat(fileData))); ++ stream.on('data', (data) => fileData.push(data)); ++ }); ++}; ++ ++const extractTar = async (tarPath: string, targetPath: string, options: IExtractOptions = {}, token: CancellationToken): Promise => { ++ return new Promise((resolve, reject): void => { ++ const sourcePathRegex = new RegExp(options.sourcePath ? `^${options.sourcePath}` : ''); ++ const extractor = tarStream.extract(); ++ const fail = (error: Error) => { ++ extractor.destroy(); ++ reject(error); ++ }; ++ extractor.once('error', fail); ++ extractor.on('entry', async (header, stream, next) => { ++ const nextEntry = (): void => { ++ stream.on('end', () => next()); ++ stream.resume(); ++ }; ++ ++ const rawName = path.normalize(header.name); ++ if (token.isCancellationRequested || !sourcePathRegex.test(rawName)) { ++ return nextEntry(); ++ } ++ ++ const fileName = rawName.replace(sourcePathRegex, ''); ++ const targetFileName = path.join(targetPath, fileName); ++ if (/\/$/.test(fileName)) { ++ return mkdirp(targetFileName).then(nextEntry); ++ } ++ ++ const dirName = path.dirname(fileName); ++ const targetDirName = path.join(targetPath, dirName); ++ if (targetDirName.indexOf(targetPath) !== 0) { ++ return fail(new Error(nls.localize('invalid file', 'Error extracting {0}. Invalid file.', fileName))); ++ } ++ ++ await mkdirp(targetDirName, undefined); ++ ++ const fstream = fs.createWriteStream(targetFileName, { mode: header.mode }); ++ fstream.once('close', () => next()); ++ fstream.once('error', fail); ++ stream.pipe(fstream); ++ }); ++ extractor.once('finish', resolve); ++ fs.createReadStream(tarPath).pipe(extractor); ++ }); ++}; ++ ++/** ++ * Override original functionality so we can use a custom marketplace with ++ * either tars or zips. ++ */ ++export const enableCustomMarketplace = (): void => { ++ (product).extensionsGallery = { // Use `any` to override readonly. ++ serviceUrl: process.env.SERVICE_URL || 'https://extensions.coder.com/api', ++ itemUrl: process.env.ITEM_URL || '', ++ controlUrl: '', ++ recommendationsUrl: '', ++ ...(product.extensionsGallery || {}), ++ }; ++ ++ const target = vszip as typeof vszip; ++ target.zip = tar; ++ target.extract = extract; ++ target.buffer = buffer; ++}; +diff --git a/src/vs/server/node/nls.ts b/src/vs/server/node/nls.ts +new file mode 100644 +index 0000000000..3d428a57d3 +--- /dev/null ++++ b/src/vs/server/node/nls.ts +@@ -0,0 +1,88 @@ ++import * as fs from 'fs'; ++import * as path from 'path'; ++import * as util from 'util'; ++import { getPathFromAmdModule } from 'vs/base/common/amd'; ++import * as lp from 'vs/base/node/languagePacks'; ++import product from 'vs/platform/product/common/product'; ++import { Translations } from 'vs/workbench/services/extensions/common/extensionPoints'; ++ ++const configurations = new Map>(); ++const metadataPath = path.join(getPathFromAmdModule(require, ''), 'nls.metadata.json'); ++ ++export const isInternalConfiguration = (config: lp.NLSConfiguration): config is lp.InternalNLSConfiguration => { ++ return config && !!(config)._languagePackId; ++}; ++ ++const DefaultConfiguration = { ++ locale: 'en', ++ availableLanguages: {}, ++}; ++ ++export const getNlsConfiguration = async (locale: string, userDataPath: string): Promise => { ++ const id = `${locale}: ${userDataPath}`; ++ if (!configurations.has(id)) { ++ configurations.set(id, new Promise(async (resolve) => { ++ const config = product.commit && await util.promisify(fs.exists)(metadataPath) ++ ? await lp.getNLSConfiguration(product.commit, userDataPath, metadataPath, locale) ++ : DefaultConfiguration; ++ if (isInternalConfiguration(config)) { ++ config._languagePackSupport = true; ++ } ++ // If the configuration has no results keep trying since code-server ++ // doesn't restart when a language is installed so this result would ++ // persist (the plugin might not be installed yet or something). ++ if (config.locale !== 'en' && config.locale !== 'en-us' && Object.keys(config.availableLanguages).length === 0) { ++ configurations.delete(id); ++ } ++ resolve(config); ++ })); ++ } ++ return configurations.get(id)!; ++}; ++ ++export const getTranslations = async (locale: string, userDataPath: string): Promise => { ++ const config = await getNlsConfiguration(locale, userDataPath); ++ if (isInternalConfiguration(config)) { ++ try { ++ return JSON.parse(await util.promisify(fs.readFile)(config._translationsConfigFile, 'utf8')); ++ } catch (error) { /* Nothing yet. */} ++ } ++ return {}; ++}; ++ ++export const getLocaleFromConfig = async (userDataPath: string): Promise => { ++ const files = ['locale.json', 'argv.json']; ++ for (let i = 0; i < files.length; ++i) { ++ try { ++ const localeConfigUri = path.join(userDataPath, 'User', files[i]); ++ const content = stripComments(await util.promisify(fs.readFile)(localeConfigUri, 'utf8')); ++ return JSON.parse(content).locale; ++ } catch (error) { /* Ignore. */ } ++ } ++ return 'en'; ++}; ++ ++// Taken from src/main.js in the main VS Code source. ++const stripComments = (content: string): string => { ++ const regexp = /('(?:[^\\']*(?:\\.)?)*')|('(?:[^\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g; ++ ++ return content.replace(regexp, (match, _m1, _m2, m3, m4) => { ++ // Only one of m1, m2, m3, m4 matches ++ if (m3) { ++ // A block comment. Replace with nothing ++ return ''; ++ } else if (m4) { ++ // A line comment. If it ends in \r?\n then keep it. ++ const length_1 = m4.length; ++ if (length_1 > 2 && m4[length_1 - 1] === '\n') { ++ return m4[length_1 - 2] === '\r' ? '\r\n' : '\n'; ++ } ++ else { ++ return ''; ++ } ++ } else { ++ // We match a string ++ return match; ++ } ++ }); ++}; +diff --git a/src/vs/server/node/protocol.ts b/src/vs/server/node/protocol.ts +new file mode 100644 +index 0000000000..3c74512192 +--- /dev/null ++++ b/src/vs/server/node/protocol.ts +@@ -0,0 +1,73 @@ ++import * as net from 'net'; ++import { VSBuffer } from 'vs/base/common/buffer'; ++import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net'; ++import { NodeSocket, WebSocketNodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; ++import { AuthRequest, ConnectionTypeRequest, HandshakeMessage } from 'vs/platform/remote/common/remoteAgentConnection'; ++ ++export interface SocketOptions { ++ readonly reconnectionToken: string; ++ readonly reconnection: boolean; ++ readonly skipWebSocketFrames: boolean; ++} ++ ++export class Protocol extends PersistentProtocol { ++ public constructor(socket: net.Socket, public readonly options: SocketOptions) { ++ super( ++ options.skipWebSocketFrames ++ ? new NodeSocket(socket) ++ : new WebSocketNodeSocket(new NodeSocket(socket)), ++ ); ++ } ++ ++ public getUnderlyingSocket(): net.Socket { ++ const socket = this.getSocket(); ++ return socket instanceof NodeSocket ++ ? socket.socket ++ : (socket as WebSocketNodeSocket).socket.socket; ++ } ++ ++ /** ++ * Perform a handshake to get a connection request. ++ */ ++ public handshake(): Promise { ++ return new Promise((resolve, reject) => { ++ const handler = this.onControlMessage((rawMessage) => { ++ try { ++ const message = JSON.parse(rawMessage.toString()); ++ switch (message.type) { ++ case 'auth': return this.authenticate(message); ++ case 'connectionType': ++ handler.dispose(); ++ return resolve(message); ++ default: throw new Error('Unrecognized message type'); ++ } ++ } catch (error) { ++ handler.dispose(); ++ reject(error); ++ } ++ }); ++ }); ++ } ++ ++ /** ++ * TODO: This ignores the authentication process entirely for now. ++ */ ++ private authenticate(_message: AuthRequest): void { ++ this.sendMessage({ type: 'sign', data: '' }); ++ } ++ ++ /** ++ * TODO: implement. ++ */ ++ public tunnel(): void { ++ throw new Error('Tunnel is not implemented yet'); ++ } ++ ++ /** ++ * Send a handshake message. In the case of the extension host, it just sends ++ * back a debug port. ++ */ ++ public sendMessage(message: HandshakeMessage | { debugPort?: number } ): void { ++ this.sendControl(VSBuffer.fromString(JSON.stringify(message))); ++ } ++} +diff --git a/src/vs/server/node/server.ts b/src/vs/server/node/server.ts +new file mode 100644 +index 0000000000..20dbca69b2 +--- /dev/null ++++ b/src/vs/server/node/server.ts +@@ -0,0 +1,257 @@ ++import * as net from 'net'; ++import * as path from 'path'; ++import { Emitter } from 'vs/base/common/event'; ++import { Schemas } from 'vs/base/common/network'; ++import { URI } from 'vs/base/common/uri'; ++import { getMachineId } from 'vs/base/node/id'; ++import { ClientConnectionEvent, IPCServer, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; ++import { createChannelReceiver } from 'vs/base/parts/ipc/node/ipc'; ++import { LogsDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner'; ++import { main } from "vs/code/node/cliProcessMain"; ++import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; ++import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; ++import { ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc'; ++import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; ++import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; ++import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; ++import { IExtensionGalleryService, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; ++import { ExtensionManagementChannel } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; ++import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; ++import { IFileService } from 'vs/platform/files/common/files'; ++import { FileService } from 'vs/platform/files/common/fileService'; ++import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; ++import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; ++import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; ++import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; ++import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; ++import { LocalizationsService } from 'vs/platform/localizations/node/localizations'; ++import { getLogLevel, ILogService } from 'vs/platform/log/common/log'; ++import { LoggerChannel } from 'vs/platform/log/common/logIpc'; ++import { SpdLogService } from 'vs/platform/log/node/spdlogService'; ++import product from 'vs/platform/product/common/product'; ++import { IProductService } from 'vs/platform/product/common/productService'; ++import { ConnectionType, ConnectionTypeRequest } from 'vs/platform/remote/common/remoteAgentConnection'; ++import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment'; ++import { IRequestService } from 'vs/platform/request/common/request'; ++import { RequestChannel } from 'vs/platform/request/common/requestIpc'; ++import { RequestService } from 'vs/platform/request/node/requestService'; ++import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry'; ++import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; ++import { ITelemetryServiceConfig, TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; ++import { combinedAppender, LogAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; ++import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender'; ++import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties'; ++import { INodeProxyService, NodeProxyChannel } from 'vs/server/common/nodeProxy'; ++import { TelemetryChannel } from 'vs/server/common/telemetry'; ++import { Query, VscodeOptions, WorkbenchOptions } from 'vs/server/ipc'; ++import { ExtensionEnvironmentChannel, FileProviderChannel, NodeProxyService } from 'vs/server/node/channel'; ++import { Connection, ExtensionHostConnection, ManagementConnection } from 'vs/server/node/connection'; ++import { TelemetryClient } from 'vs/server/node/insights'; ++import { logger } from 'vs/server/node/logger'; ++import { getLocaleFromConfig, getNlsConfiguration } from 'vs/server/node/nls'; ++import { Protocol } from 'vs/server/node/protocol'; ++import { getUriTransformer } from 'vs/server/node/util'; ++import { REMOTE_FILE_SYSTEM_CHANNEL_NAME } from "vs/workbench/services/remote/common/remoteAgentFileSystemChannel"; ++import { RemoteExtensionLogFileName } from 'vs/workbench/services/remote/common/remoteAgentService'; ++ ++export class Vscode { ++ public readonly _onDidClientConnect = new Emitter(); ++ public readonly onDidClientConnect = this._onDidClientConnect.event; ++ private readonly ipc = new IPCServer(this.onDidClientConnect); ++ ++ private readonly maxExtraOfflineConnections = 0; ++ private readonly connections = new Map>(); ++ ++ private readonly services = new ServiceCollection(); ++ private servicesPromise?: Promise; ++ ++ public async cli(args: ParsedArgs): Promise { ++ return main(args); ++ } ++ ++ public async initialize(options: VscodeOptions): Promise { ++ const transformer = getUriTransformer(options.remoteAuthority); ++ if (!this.servicesPromise) { ++ this.servicesPromise = this.initializeServices(options.args); ++ } ++ await this.servicesPromise; ++ const environment = this.services.get(IEnvironmentService) as IEnvironmentService; ++ const startPath = options.startPath; ++ return { ++ workbenchWebConfiguration: { ++ workspaceUri: startPath && startPath.workspace ? URI.parse(startPath.url) : undefined, ++ folderUri: startPath && !startPath.workspace ? URI.parse(startPath.url) : undefined, ++ remoteAuthority: options.remoteAuthority, ++ logLevel: getLogLevel(environment), ++ }, ++ remoteUserDataUri: transformer.transformOutgoing(URI.file(environment.userDataPath)), ++ productConfiguration: product, ++ nlsConfiguration: await getNlsConfiguration(environment.args.locale || await getLocaleFromConfig(environment.userDataPath), environment.userDataPath), ++ commit: product.commit || 'development', ++ }; ++ } ++ ++ public async handleWebSocket(socket: net.Socket, query: Query): Promise { ++ if (!query.reconnectionToken) { ++ throw new Error('Reconnection token is missing from query parameters'); ++ } ++ const protocol = new Protocol(socket, { ++ reconnectionToken: query.reconnectionToken, ++ reconnection: query.reconnection === 'true', ++ skipWebSocketFrames: query.skipWebSocketFrames === 'true', ++ }); ++ try { ++ await this.connect(await protocol.handshake(), protocol); ++ } catch (error) { ++ protocol.sendMessage({ type: 'error', reason: error.message }); ++ protocol.dispose(); ++ protocol.getSocket().dispose(); ++ } ++ return true; ++ } ++ ++ private async connect(message: ConnectionTypeRequest, protocol: Protocol): Promise { ++ if (product.commit && message.commit !== product.commit) { ++ logger.warn(`Version mismatch (${message.commit} instead of ${product.commit})`); ++ } ++ ++ switch (message.desiredConnectionType) { ++ case ConnectionType.ExtensionHost: ++ case ConnectionType.Management: ++ if (!this.connections.has(message.desiredConnectionType)) { ++ this.connections.set(message.desiredConnectionType, new Map()); ++ } ++ const connections = this.connections.get(message.desiredConnectionType)!; ++ ++ const ok = async () => { ++ return message.desiredConnectionType === ConnectionType.ExtensionHost ++ ? { debugPort: await this.getDebugPort() } ++ : { type: 'ok' }; ++ }; ++ ++ const token = protocol.options.reconnectionToken; ++ if (protocol.options.reconnection && connections.has(token)) { ++ protocol.sendMessage(await ok()); ++ const buffer = protocol.readEntireBuffer(); ++ protocol.dispose(); ++ return connections.get(token)!.reconnect(protocol.getSocket(), buffer); ++ } else if (protocol.options.reconnection || connections.has(token)) { ++ throw new Error(protocol.options.reconnection ++ ? 'Unrecognized reconnection token' ++ : 'Duplicate reconnection token' ++ ); ++ } ++ ++ protocol.sendMessage(await ok()); ++ ++ let connection: Connection; ++ if (message.desiredConnectionType === ConnectionType.Management) { ++ connection = new ManagementConnection(protocol, token); ++ this._onDidClientConnect.fire({ ++ protocol, onDidClientDisconnect: connection.onClose, ++ }); ++ // TODO: Need a way to match clients with a connection. For now ++ // dispose everything which only works because no extensions currently ++ // utilize long-running proxies. ++ (this.services.get(INodeProxyService) as NodeProxyService)._onUp.fire(); ++ connection.onClose(() => (this.services.get(INodeProxyService) as NodeProxyService)._onDown.fire()); ++ } else { ++ const buffer = protocol.readEntireBuffer(); ++ connection = new ExtensionHostConnection( ++ message.args ? message.args.language : 'en', ++ protocol, buffer, token, ++ this.services.get(ILogService) as ILogService, ++ this.services.get(IEnvironmentService) as IEnvironmentService, ++ ); ++ } ++ connections.set(token, connection); ++ connection.onClose(() => connections.delete(token)); ++ this.disposeOldOfflineConnections(connections); ++ break; ++ case ConnectionType.Tunnel: return protocol.tunnel(); ++ default: throw new Error('Unrecognized connection type'); ++ } ++ } ++ ++ private disposeOldOfflineConnections(connections: Map): void { ++ const offline = Array.from(connections.values()) ++ .filter((connection) => typeof connection.offline !== 'undefined'); ++ for (let i = 0, max = offline.length - this.maxExtraOfflineConnections; i < max; ++i) { ++ offline[i].dispose(); ++ } ++ } ++ ++ private async initializeServices(args: ParsedArgs): Promise { ++ const environmentService = new EnvironmentService(args, process.execPath); ++ const logService = new SpdLogService(RemoteExtensionLogFileName, environmentService.logsPath, getLogLevel(environmentService)); ++ const fileService = new FileService(logService); ++ fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(logService)); ++ ++ const piiPaths = [ ++ path.join(environmentService.userDataPath, 'clp'), // Language packs. ++ environmentService.extensionsPath, ++ environmentService.builtinExtensionsPath, ++ ...environmentService.extraExtensionPaths, ++ ...environmentService.extraBuiltinExtensionPaths, ++ ]; ++ ++ this.ipc.registerChannel('logger', new LoggerChannel(logService)); ++ this.ipc.registerChannel(ExtensionHostDebugBroadcastChannel.ChannelName, new ExtensionHostDebugBroadcastChannel()); ++ ++ this.services.set(ILogService, logService); ++ this.services.set(IEnvironmentService, environmentService); ++ this.services.set(IConfigurationService, new SyncDescriptor(ConfigurationService, [environmentService.machineSettingsResource])); ++ this.services.set(IRequestService, new SyncDescriptor(RequestService)); ++ this.services.set(IFileService, fileService); ++ this.services.set(IProductService, { _serviceBrand: undefined, ...product }); ++ this.services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); ++ this.services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); ++ ++ if (!environmentService.args['disable-telemetry']) { ++ this.services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [{ ++ appender: combinedAppender( ++ new AppInsightsAppender('code-server', null, () => new TelemetryClient() as any, logService), ++ new LogAppender(logService), ++ ), ++ commonProperties: resolveCommonProperties( ++ product.commit, product.version, await getMachineId(), ++ [], environmentService.installSourcePath, 'code-server', ++ ), ++ piiPaths, ++ } as ITelemetryServiceConfig])); ++ } else { ++ this.services.set(ITelemetryService, NullTelemetryService); ++ } ++ ++ await new Promise((resolve) => { ++ const instantiationService = new InstantiationService(this.services); ++ this.services.set(ILocalizationsService, instantiationService.createInstance(LocalizationsService)); ++ this.services.set(INodeProxyService, instantiationService.createInstance(NodeProxyService)); ++ ++ instantiationService.invokeFunction(() => { ++ instantiationService.createInstance(LogsDataCleaner); ++ const telemetryService = this.services.get(ITelemetryService) as ITelemetryService; ++ this.ipc.registerChannel('extensions', new ExtensionManagementChannel( ++ this.services.get(IExtensionManagementService) as IExtensionManagementService, ++ (context) => getUriTransformer(context.remoteAuthority), ++ )); ++ this.ipc.registerChannel('remoteextensionsenvironment', new ExtensionEnvironmentChannel( ++ environmentService, logService, telemetryService, '', ++ )); ++ this.ipc.registerChannel('request', new RequestChannel(this.services.get(IRequestService) as IRequestService)); ++ this.ipc.registerChannel('telemetry', new TelemetryChannel(telemetryService)); ++ this.ipc.registerChannel('nodeProxy', new NodeProxyChannel(this.services.get(INodeProxyService) as INodeProxyService)); ++ this.ipc.registerChannel('localizations', >createChannelReceiver(this.services.get(ILocalizationsService) as ILocalizationsService)); ++ this.ipc.registerChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME, new FileProviderChannel(environmentService, logService)); ++ resolve(new ErrorTelemetry(telemetryService)); ++ }); ++ }); ++ } ++ ++ /** ++ * TODO: implement. ++ */ ++ private async getDebugPort(): Promise { ++ return undefined; ++ } ++} +diff --git a/src/vs/server/node/uriTransformer.js b/src/vs/server/node/uriTransformer.js +new file mode 100644 +index 0000000000..fc69441cf0 +--- /dev/null ++++ b/src/vs/server/node/uriTransformer.js +@@ -0,0 +1,24 @@ ++// This file is included via a regular Node require. I'm not sure how (or if) ++// we can write this in Typescript and have it compile to non-AMD syntax. ++module.exports = (remoteAuthority) => { ++ return { ++ transformIncoming: (uri) => { ++ switch (uri.scheme) { ++ case "vscode-remote": return { scheme: "file", path: uri.path }; ++ default: return uri; ++ } ++ }, ++ transformOutgoing: (uri) => { ++ switch (uri.scheme) { ++ case "file": return { scheme: "vscode-remote", authority: remoteAuthority, path: uri.path }; ++ default: return uri; ++ } ++ }, ++ transformOutgoingScheme: (scheme) => { ++ switch (scheme) { ++ case "file": return "vscode-remote"; ++ default: return scheme; ++ } ++ }, ++ }; ++}; +diff --git a/src/vs/server/node/util.ts b/src/vs/server/node/util.ts +new file mode 100644 +index 0000000000..06b080044c +--- /dev/null ++++ b/src/vs/server/node/util.ts +@@ -0,0 +1,9 @@ ++import { getPathFromAmdModule } from 'vs/base/common/amd'; ++import { URITransformer, IRawURITransformer } from 'vs/base/common/uriIpc'; ++ ++export const uriTransformerPath = getPathFromAmdModule(require, 'vs/server/node/uriTransformer'); ++export const getUriTransformer = (remoteAuthority: string): URITransformer => { ++ const rawURITransformerFactory = require.__$__nodeRequire(uriTransformerPath); ++ const rawURITransformer = rawURITransformerFactory(remoteAuthority); ++ return new URITransformer(rawURITransformer); ++}; +diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts +index e69aa80159..71a899d37b 100644 +--- a/src/vs/workbench/api/browser/extensionHost.contribution.ts ++++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts +@@ -58,6 +58,7 @@ import './mainThreadWorkspace'; + import './mainThreadComments'; + import './mainThreadTask'; + import './mainThreadLabelService'; ++import 'vs/server/browser/mainThreadNodeProxy'; + import './mainThreadTunnelService'; + import './mainThreadAuthentication'; + import './mainThreadTimeline'; +diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts +index 91045fcda6..a41624e3d2 100644 +--- a/src/vs/workbench/api/common/extHost.api.impl.ts ++++ b/src/vs/workbench/api/common/extHost.api.impl.ts +@@ -67,6 +67,7 @@ import { ILogService } from 'vs/platform/log/common/log'; + import { IURITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService'; + import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; + import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; ++import { IExtHostNodeProxy } from 'vs/server/browser/extHostNodeProxy'; + import { ExtHostTheming } from 'vs/workbench/api/common/extHostTheming'; + import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService'; + import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; +@@ -91,6 +92,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I + const rpcProtocol = accessor.get(IExtHostRpcService); + const extHostStorage = accessor.get(IExtHostStorage); + const extHostLogService = accessor.get(ILogService); ++ const extHostNodeProxy = accessor.get(IExtHostNodeProxy); + const extHostTunnelService = accessor.get(IExtHostTunnelService); + const extHostApiDeprecation = accessor.get(IExtHostApiDeprecationService); + +@@ -100,6 +102,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I + rpcProtocol.set(ExtHostContext.ExtHostConfiguration, extHostConfiguration); + rpcProtocol.set(ExtHostContext.ExtHostExtensionService, extensionService); + rpcProtocol.set(ExtHostContext.ExtHostStorage, extHostStorage); ++ rpcProtocol.set(ExtHostContext.ExtHostNodeProxy, extHostNodeProxy); + rpcProtocol.set(ExtHostContext.ExtHostTunnelService, extHostTunnelService); + + // automatically create and register addressable instances +diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts +index 55130ff918..35ae724c4f 100644 +--- a/src/vs/workbench/api/common/extHost.protocol.ts ++++ b/src/vs/workbench/api/common/extHost.protocol.ts +@@ -667,6 +667,16 @@ export interface MainThreadLabelServiceShape extends IDisposable { + $unregisterResourceLabelFormatter(handle: number): void; + } + ++export interface MainThreadNodeProxyShape extends IDisposable { ++ $send(message: string): void; ++} ++export interface ExtHostNodeProxyShape { ++ $onMessage(message: string): void; ++ $onClose(): void; ++ $onDown(): void; ++ $onUp(): void; ++} ++ + export interface MainThreadSearchShape extends IDisposable { + $registerFileSearchProvider(handle: number, scheme: string): void; + $registerTextSearchProvider(handle: number, scheme: string): void; +@@ -1496,6 +1506,7 @@ export const MainContext = { + MainThreadTask: createMainId('MainThreadTask'), + MainThreadWindow: createMainId('MainThreadWindow'), + MainThreadLabelService: createMainId('MainThreadLabelService'), ++ MainThreadNodeProxy: createMainId('MainThreadNodeProxy'), + MainThreadTheming: createMainId('MainThreadTheming'), + MainThreadTunnelService: createMainId('MainThreadTunnelService'), + MainThreadTimeline: createMainId('MainThreadTimeline') +@@ -1533,6 +1544,7 @@ export const ExtHostContext = { + ExtHostUrls: createExtId('ExtHostUrls'), + ExtHostOutputService: createMainId('ExtHostOutputService'), + ExtHostLabelService: createMainId('ExtHostLabelService'), ++ ExtHostNodeProxy: createMainId('ExtHostNodeProxy'), + ExtHostTheming: createMainId('ExtHostTheming'), + ExtHostTunnelService: createMainId('ExtHostTunnelService'), + ExtHostAuthentication: createMainId('ExtHostAuthentication'), +diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts +index 978bf32fcd..809b51227c 100644 +--- a/src/vs/workbench/api/common/extHostExtensionService.ts ++++ b/src/vs/workbench/api/common/extHostExtensionService.ts +@@ -5,7 +5,7 @@ + + import * as nls from 'vs/nls'; + import * as path from 'vs/base/common/path'; +-import { originalFSPath, joinPath } from 'vs/base/common/resources'; ++import { originalFSPath } from 'vs/base/common/resources'; + import { Barrier } from 'vs/base/common/async'; + import { dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; + import { TernarySearchTree } from 'vs/base/common/map'; +@@ -32,6 +32,7 @@ import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitData + import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; + import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; + import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; ++import { IExtHostNodeProxy } from 'vs/server/browser/extHostNodeProxy'; + import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService'; + + interface ITestRunner { +@@ -77,6 +78,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio + protected readonly _extHostWorkspace: ExtHostWorkspace; + protected readonly _extHostConfiguration: ExtHostConfiguration; + protected readonly _logService: ILogService; ++ protected readonly _nodeProxy: IExtHostNodeProxy; + protected readonly _extHostTunnelService: IExtHostTunnelService; + + protected readonly _mainThreadWorkspaceProxy: MainThreadWorkspaceShape; +@@ -107,7 +109,8 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio + @ILogService logService: ILogService, + @IExtHostInitDataService initData: IExtHostInitDataService, + @IExtensionStoragePaths storagePath: IExtensionStoragePaths, +- @IExtHostTunnelService extHostTunnelService: IExtHostTunnelService ++ @IExtHostNodeProxy nodeProxy: IExtHostNodeProxy, ++ @IExtHostTunnelService extHostTunnelService: IExtHostTunnelService, + ) { + this._hostUtils = hostUtils; + this._extHostContext = extHostContext; +@@ -116,6 +119,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio + this._extHostWorkspace = extHostWorkspace; + this._extHostConfiguration = extHostConfiguration; + this._logService = logService; ++ this._nodeProxy = nodeProxy; + this._extHostTunnelService = extHostTunnelService; + this._disposables = new DisposableStore(); + +@@ -341,14 +345,14 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio + + const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup); + return Promise.all([ +- this._loadCommonJSModule(joinPath(extensionDescription.extensionLocation, extensionDescription.main), activationTimesBuilder), ++ this._loadCommonJSModule(extensionDescription, activationTimesBuilder), + this._loadExtensionContext(extensionDescription) + ]).then(values => { + return AbstractExtHostExtensionService._callActivate(this._logService, extensionDescription.identifier, values[0], values[1], activationTimesBuilder); + }); + } + +- protected abstract _loadCommonJSModule(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise; ++ protected abstract _loadCommonJSModule(module: URI | IExtensionDescription, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise; + + private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise { + +diff --git a/src/vs/workbench/api/node/extHost.services.ts b/src/vs/workbench/api/node/extHost.services.ts +index 72ad75d63e..07b8a3f20c 100644 +--- a/src/vs/workbench/api/node/extHost.services.ts ++++ b/src/vs/workbench/api/node/extHost.services.ts +@@ -24,6 +24,8 @@ import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePa + import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; + import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService'; + import { IExtHostStorage, ExtHostStorage } from 'vs/workbench/api/common/extHostStorage'; ++import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; ++import { IExtHostNodeProxy } from 'vs/server/browser/extHostNodeProxy'; + import { ILogService } from 'vs/platform/log/common/log'; + import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService'; + import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService'; +@@ -47,3 +49,19 @@ registerSingleton(IExtensionStoragePaths, ExtensionStoragePaths); + registerSingleton(IExtHostExtensionService, ExtHostExtensionService); + registerSingleton(IExtHostStorage, ExtHostStorage); + registerSingleton(IExtHostTunnelService, ExtHostTunnelService); ++ ++function NotImplementedProxy(name: ServiceIdentifier): { new(): T } { ++ return class { ++ constructor() { ++ return new Proxy({}, { ++ get(target: any, prop: string | number) { ++ if (target[prop]) { ++ return target[prop]; ++ } ++ throw new Error(`Not Implemented: ${name}->${String(prop)}`); ++ } ++ }); ++ } ++ }; ++} ++registerSingleton(IExtHostNodeProxy, class extends NotImplementedProxy(IExtHostNodeProxy) {}); +diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts +index a1c3e50ffd..910627aaf9 100644 +--- a/src/vs/workbench/api/node/extHostExtensionService.ts ++++ b/src/vs/workbench/api/node/extHostExtensionService.ts +@@ -13,6 +13,8 @@ import { ExtHostDownloadService } from 'vs/workbench/api/node/extHostDownloadSer + import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer'; + import { URI } from 'vs/base/common/uri'; + import { Schemas } from 'vs/base/common/network'; ++import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; ++import { joinPath } from 'vs/base/common/resources'; + + class NodeModuleRequireInterceptor extends RequireInterceptor { + +@@ -76,7 +78,10 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { + }; + } + +- protected _loadCommonJSModule(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { ++ protected _loadCommonJSModule(module: URI | IExtensionDescription, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { ++ if (!URI.isUri(module)) { ++ module = joinPath(module.extensionLocation, module.main!); ++ } + if (module.scheme !== Schemas.file) { + throw new Error(`Cannot load URI: '${module}', must be of file-scheme`); + } +diff --git a/src/vs/workbench/api/node/extHostStoragePaths.ts b/src/vs/workbench/api/node/extHostStoragePaths.ts +index afdd6bf398..1633daf93d 100644 +--- a/src/vs/workbench/api/node/extHostStoragePaths.ts ++++ b/src/vs/workbench/api/node/extHostStoragePaths.ts +@@ -5,13 +5,14 @@ + + import * as path from 'vs/base/common/path'; + import { URI } from 'vs/base/common/uri'; +-import * as pfs from 'vs/base/node/pfs'; +-import { IEnvironment, IStaticWorkspaceData } from 'vs/workbench/api/common/extHost.protocol'; ++import { IEnvironment, IStaticWorkspaceData, MainContext } from 'vs/workbench/api/common/extHost.protocol'; + import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; + import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; + import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; + import { withNullAsUndefined } from 'vs/base/common/types'; + import { ILogService } from 'vs/platform/log/common/log'; ++import { IExtHostRpcService } from '../common/extHostRpcService'; ++import { VSBuffer } from 'vs/base/common/buffer'; + + export class ExtensionStoragePaths implements IExtensionStoragePaths { + +@@ -26,6 +27,7 @@ export class ExtensionStoragePaths implements IExtensionStoragePaths { + constructor( + @IExtHostInitDataService initData: IExtHostInitDataService, + @ILogService private readonly _logService: ILogService, ++ @IExtHostRpcService private readonly _extHostRpc: IExtHostRpcService, + ) { + this._workspace = withNullAsUndefined(initData.workspace); + this._environment = initData.environment; +@@ -54,21 +56,26 @@ export class ExtensionStoragePaths implements IExtensionStoragePaths { + const storageName = this._workspace.id; + const storagePath = path.join(this._environment.appSettingsHome.fsPath, 'workspaceStorage', storageName); + +- const exists = await pfs.dirExists(storagePath); +- +- if (exists) { ++ // NOTE@coder: Use the file system proxy so this will work in the browser. ++ const fileSystem = this._extHostRpc.getProxy(MainContext.MainThreadFileSystem); ++ try { ++ await fileSystem.$stat(URI.file(storagePath)); + return storagePath; ++ } catch (error) { ++ // Doesn't exist. + } + + try { +- await pfs.mkdirp(storagePath); +- await pfs.writeFile( +- path.join(storagePath, 'meta.json'), +- JSON.stringify({ +- id: this._workspace.id, +- configuration: this._workspace.configuration && URI.revive(this._workspace.configuration).toString(), +- name: this._workspace.name +- }, undefined, 2) ++ // NOTE@coder: $writeFile performs a mkdirp. ++ await fileSystem.$writeFile( ++ URI.file(path.join(storagePath, 'meta.json')), ++ VSBuffer.fromString( ++ JSON.stringify({ ++ id: this._workspace.id, ++ configuration: this._workspace.configuration && URI.revive(this._workspace.configuration).toString(), ++ name: this._workspace.name ++ }, undefined, 2) ++ ) + ); + return storagePath; + +diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts +index 4781f22676..86c9246f51 100644 +--- a/src/vs/workbench/api/worker/extHostExtensionService.ts ++++ b/src/vs/workbench/api/worker/extHostExtensionService.ts +@@ -9,6 +9,9 @@ import { AbstractExtHostExtensionService } from 'vs/workbench/api/common/extHost + import { endsWith } from 'vs/base/common/strings'; + import { URI } from 'vs/base/common/uri'; + import { RequireInterceptor } from 'vs/workbench/api/common/extHostRequireInterceptor'; ++import { joinPath } from 'vs/base/common/resources'; ++import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; ++import { loadCommonJSModule } from 'vs/server/browser/worker'; + + class WorkerRequireInterceptor extends RequireInterceptor { + +@@ -41,7 +44,14 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { + await this._fakeModules.install(); + } + +- protected async _loadCommonJSModule(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { ++ protected async _loadCommonJSModule(module: URI | IExtensionDescription, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { ++ if (!URI.isUri(module) && module.extensionKind !== 'web') { ++ return loadCommonJSModule(module, activationTimesBuilder, this._nodeProxy, this._logService, this._fakeModules!.getModule('vscode', module.extensionLocation)); ++ } ++ ++ if (!URI.isUri(module)) { ++ module = joinPath(module.extensionLocation, module.main!); ++ } + + module = module.with({ path: ensureSuffix(module.path, '.js') }); + const response = await fetch(module.toString(true)); +@@ -57,7 +67,7 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { + const _exports = {}; + const _module = { exports: _exports }; + const _require = (request: string) => { +- const result = this._fakeModules!.getModule(request, module); ++ const result = this._fakeModules!.getModule(request, module); + if (result === undefined) { + throw new Error(`Cannot load module '${request}'`); + } +diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts +index 94e7052574..4e83208017 100644 +--- a/src/vs/workbench/browser/web.main.ts ++++ b/src/vs/workbench/browser/web.main.ts +@@ -49,6 +49,7 @@ import { IndexedDBLogProvider } from 'vs/workbench/services/log/browser/indexedD + import { InMemoryLogProvider } from 'vs/workbench/services/log/common/inMemoryLogProvider'; + import { isWorkspaceToOpen, isFolderToOpen } from 'vs/platform/windows/common/windows'; + import { getWorkspaceIdentifier } from 'vs/workbench/services/workspaces/browser/workspaces'; ++import { initialize } from 'vs/server/browser/client'; + + class BrowserMain extends Disposable { + +@@ -85,6 +86,7 @@ class BrowserMain extends Disposable { + + // Startup + workbench.startup(); ++ await initialize(services.serviceCollection); + } + + private registerListeners(workbench: Workbench, storageService: BrowserStorageService): void { +diff --git a/src/vs/workbench/common/resources.ts b/src/vs/workbench/common/resources.ts +index c509716fc4..2b4c847d1e 100644 +--- a/src/vs/workbench/common/resources.ts ++++ b/src/vs/workbench/common/resources.ts +@@ -15,6 +15,7 @@ import { ParsedExpression, IExpression, parse } from 'vs/base/common/glob'; + import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; + import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; + import { withNullAsUndefined } from 'vs/base/common/types'; ++import { Schemas } from 'vs/base/common/network'; + + export class ResourceContextKey extends Disposable implements IContextKey { + +@@ -63,7 +64,8 @@ export class ResourceContextKey extends Disposable implements IContextKey { + set(value: URI | null) { + if (!ResourceContextKey._uriEquals(this._resourceKey.get(), value)) { + this._resourceKey.set(value); +- this._schemeKey.set(value ? value.scheme : null); ++ // NOTE@coder: Fixes extensions matching against file schemas. ++ this._schemeKey.set(value ? (value.scheme === Schemas.vscodeRemote ? Schemas.file : value.scheme) : null); + this._filenameKey.set(value ? basename(value) : null); + this._langIdKey.set(value ? this._modeService.getModeIdByFilepathOrFirstLine(value) : null); + this._extensionKey.set(value ? extname(value) : null); +diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js +index 63c9af47e2..021358fef9 100644 +--- a/src/vs/workbench/contrib/webview/browser/pre/main.js ++++ b/src/vs/workbench/contrib/webview/browser/pre/main.js +@@ -329,7 +329,8 @@ + if (data.endpoint) { + try { + const endpointUrl = new URL(data.endpoint); +- csp.setAttribute('content', csp.getAttribute('content').replace(/vscode-resource:(?=(\s|;|$))/g, endpointUrl.origin)); ++ // NOTE@coder: Add back the trailing slash so it'll work for sub-paths. ++ csp.setAttribute('content', csp.getAttribute('content').replace(/vscode-resource:(?=(\s|;|$))/g, endpointUrl.origin + "/")); + } catch (e) { + console.error('Could not rewrite csp'); + } +diff --git a/src/vs/workbench/services/dialogs/browser/dialogService.ts b/src/vs/workbench/services/dialogs/browser/dialogService.ts +index f67f9aa064..add754cd5a 100644 +--- a/src/vs/workbench/services/dialogs/browser/dialogService.ts ++++ b/src/vs/workbench/services/dialogs/browser/dialogService.ts +@@ -122,11 +122,12 @@ export class DialogService implements IDialogService { + + async about(): Promise { + const detail = nls.localize('aboutDetail', +- "Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}", ++ "Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}\nCode Server Version: {4}", + this.productService.version || 'Unknown', + this.productService.commit || 'Unknown', + this.productService.date || 'Unknown', +- navigator.userAgent ++ navigator.userAgent, ++ this.productService.codeServerVersion || 'Unknown', + ); + + const { choice } = await this.show(Severity.Info, this.productService.nameLong, [nls.localize('copy', "Copy"), nls.localize('ok', "OK")], { detail, cancelId: 1 }); +diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts +index 1bf4cfad2a..924a2fcd87 100644 +--- a/src/vs/workbench/services/environment/browser/environmentService.ts ++++ b/src/vs/workbench/services/environment/browser/environmentService.ts +@@ -195,8 +195,8 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment + + @memoize + get webviewExternalEndpoint(): string { +- // TODO: get fallback from product.json +- return (this.options.webviewEndpoint || 'https://{{uuid}}.vscode-webview-test.com/{{commit}}').replace('{{commit}}', product.commit || '0d728c31ebdf03869d2687d9be0b017667c9ff37'); ++ // NOTE@coder: Modified to work against the current URL. ++ return `${window.location.origin}${window.location.pathname.replace(/\/+$/, '')}/webview/`; + } + + @memoize +@@ -249,6 +249,8 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment + installSourcePath!: string; + + builtinExtensionsPath!: string; ++ extraExtensionPaths!: string[]; ++ extraBuiltinExtensionPaths!: string[]; + + globalStorageHome!: string; + workspaceStorageHome!: string; +diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts +index fe891a042e..21d0d4bf61 100644 +--- a/src/vs/workbench/services/extensions/browser/extensionService.ts ++++ b/src/vs/workbench/services/extensions/browser/extensionService.ts +@@ -119,6 +119,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten + + } else { + // remote: only enabled and none-web'ish extension ++ localExtensions.push(...remoteEnv.extensions.filter(extension => this._isEnabled(extension) && canExecuteOnWeb(extension, this._productService, this._configService))); + remoteEnv.extensions = remoteEnv.extensions.filter(extension => this._isEnabled(extension) && !canExecuteOnWeb(extension, this._productService, this._configService)); + this._checkEnableProposedApi(remoteEnv.extensions); + +diff --git a/src/vs/workbench/services/extensions/common/extensionsUtil.ts b/src/vs/workbench/services/extensions/common/extensionsUtil.ts +index 9e8352ac88..22a2d296f9 100644 +--- a/src/vs/workbench/services/extensions/common/extensionsUtil.ts ++++ b/src/vs/workbench/services/extensions/common/extensionsUtil.ts +@@ -32,7 +32,8 @@ export function canExecuteOnWorkspace(manifest: IExtensionManifest, productServi + + export function canExecuteOnWeb(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean { + const extensionKind = getExtensionKind(manifest, productService, configurationService); +- return extensionKind.some(kind => kind === 'web'); ++ // NOTE@coder: Hardcode vim for now. ++ return extensionKind.some(kind => kind === 'web') || manifest.name === 'vim'; + } + + export function getExtensionKind(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): ExtensionKind[] { +diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts +index 0f35c54431..32fff09b18 100644 +--- a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts ++++ b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts +@@ -53,12 +53,13 @@ const args = minimist(process.argv.slice(2), { + const Module = require.__$__nodeRequire('module') as any; + const originalLoad = Module._load; + +- Module._load = function (request: string) { ++ Module._load = function (request: string, parent: object, isMain: boolean) { + if (request === 'natives') { + throw new Error('Either the extension or a NPM dependency is using the "natives" node module which is unsupported as it can cause a crash of the extension host. Click [here](https://go.microsoft.com/fwlink/?linkid=871887) to find out more'); + } + +- return originalLoad.apply(this, arguments); ++ // NOTE@coder: Map node_module.asar requests to regular node_modules. ++ return originalLoad.apply(this, [request.replace(/node_modules\.asar(\.unpacked)?/, 'node_modules'), parent, isMain]); + }; + })(); + +@@ -131,8 +132,11 @@ function _createExtHostProtocol(): Promise { + + // Wait for rich client to reconnect + protocol.onSocketClose(() => { +- // The socket has closed, let's give the renderer a certain amount of time to reconnect +- disconnectRunner1.schedule(); ++ // NOTE@coder: Inform the server so we can manage offline ++ // connections there instead. Our goal is to persist connections ++ // forever (to a reasonable point) to account for things like ++ // hibernating overnight. ++ process.send!({ type: 'VSCODE_EXTHOST_DISCONNECTED' }); + }); + } + } +diff --git a/src/vs/workbench/services/extensions/worker/extHost.services.ts b/src/vs/workbench/services/extensions/worker/extHost.services.ts +index bbb72e9511..0785d3391d 100644 +--- a/src/vs/workbench/services/extensions/worker/extHost.services.ts ++++ b/src/vs/workbench/services/extensions/worker/extHost.services.ts +@@ -18,9 +18,10 @@ import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePa + import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; + import { IExtHostStorage, ExtHostStorage } from 'vs/workbench/api/common/extHostStorage'; + import { ExtHostExtensionService } from 'vs/workbench/api/worker/extHostExtensionService'; +-import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; + import { ILogService } from 'vs/platform/log/common/log'; + import { ExtHostLogService } from 'vs/workbench/api/worker/extHostLogService'; ++import { ExtHostNodeProxy, IExtHostNodeProxy } from 'vs/server/browser/extHostNodeProxy'; ++import { ExtensionStoragePaths } from 'vs/workbench/api/node/extHostStoragePaths'; + import { IExtHostTunnelService, ExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService'; + import { IExtHostApiDeprecationService, ExtHostApiDeprecationService, } from 'vs/workbench/api/common/extHostApiDeprecationService'; + +@@ -36,24 +37,10 @@ registerSingleton(IExtHostDocumentsAndEditors, ExtHostDocumentsAndEditors); + registerSingleton(IExtHostStorage, ExtHostStorage); + registerSingleton(IExtHostExtensionService, ExtHostExtensionService); + registerSingleton(IExtHostSearch, ExtHostSearch); ++registerSingleton(IExtHostNodeProxy, ExtHostNodeProxy); + registerSingleton(IExtHostTunnelService, ExtHostTunnelService); + +-// register services that only throw errors +-function NotImplementedProxy(name: ServiceIdentifier): { new(): T } { +- return class { +- constructor() { +- return new Proxy({}, { +- get(target: any, prop: string | number) { +- if (target[prop]) { +- return target[prop]; +- } +- throw new Error(`Not Implemented: ${name}->${String(prop)}`); +- } +- }); +- } +- }; +-} + registerSingleton(IExtHostTerminalService, WorkerExtHostTerminalService); + registerSingleton(IExtHostTask, WorkerExtHostTask); + registerSingleton(IExtHostDebugService, WorkerExtHostDebugService); +-registerSingleton(IExtensionStoragePaths, class extends NotImplementedProxy(IExtensionStoragePaths) { whenReady = Promise.resolve(); }); ++registerSingleton(IExtensionStoragePaths, ExtensionStoragePaths); +diff --git a/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts b/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts +index 79455414c0..a407593b4d 100644 +--- a/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts ++++ b/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts +@@ -14,7 +14,11 @@ + + require.config({ + baseUrl: monacoBaseUrl, +- catchError: true ++ catchError: true, ++ paths: { ++ '@coder/node-browser': `../node_modules/@coder/node-browser/out/client/client.js`, ++ '@coder/requirefs': `../node_modules/@coder/requirefs/out/requirefs.js`, ++ } + }); + + require(['vs/workbench/services/extensions/worker/extensionHostWorker'], () => { }, err => console.error(err)); +diff --git a/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts b/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts +index 99394090da..4891e0fece 100644 +--- a/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts ++++ b/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts +@@ -5,17 +5,17 @@ + + import { createChannelSender } from 'vs/base/parts/ipc/node/ipc'; + import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; +-import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; + import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; ++import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; + + export class LocalizationsService { + + _serviceBrand: undefined; + + constructor( +- @ISharedProcessService sharedProcessService: ISharedProcessService, ++ @IRemoteAgentService remoteAgentService: IRemoteAgentService, + ) { +- return createChannelSender(sharedProcessService.getChannel('localizations')); ++ return createChannelSender(remoteAgentService.getConnection()!.getChannel('localizations')); + } + } + +diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts +index 0719b361e0..b9420ba206 100644 +--- a/src/vs/workbench/workbench.web.main.ts ++++ b/src/vs/workbench/workbench.web.main.ts +@@ -34,7 +34,8 @@ import 'vs/workbench/services/textfile/browser/browserTextFileService'; + import 'vs/workbench/services/keybinding/browser/keymapService'; + import 'vs/workbench/services/extensions/browser/extensionService'; + import 'vs/workbench/services/extensionManagement/common/extensionManagementServerService'; +-import 'vs/workbench/services/telemetry/browser/telemetryService'; ++// NOTE@coder: We send it all to the server side to be processed there instead. ++// import 'vs/workbench/services/telemetry/browser/telemetryService'; + import 'vs/workbench/services/configurationResolver/browser/configurationResolverService'; + import 'vs/workbench/services/credentials/browser/credentialsService'; + import 'vs/workbench/services/url/browser/urlService'; +diff --git a/test/automation/package.json b/test/automation/package.json +index 297dce969b..06e0199c74 100644 +--- a/test/automation/package.json ++++ b/test/automation/package.json +@@ -22,12 +22,12 @@ + "devDependencies": { + "@types/mkdirp": "0.5.1", + "@types/ncp": "2.0.1", +- "@types/node": "8.0.33", ++ "@types/node": "^10.12.12", + "@types/puppeteer": "^1.19.0", + "@types/tmp": "0.1.0", + "concurrently": "^3.5.1", + "cpx": "^1.5.0", +- "typescript": "2.9.2", ++ "typescript": "3.7.2", + "watch": "^1.0.2" + }, + "dependencies": { +diff --git a/test/automation/yarn.lock b/test/automation/yarn.lock +index 94a1350861..e45971c254 100644 +--- a/test/automation/yarn.lock ++++ b/test/automation/yarn.lock +@@ -21,10 +21,10 @@ + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.1.tgz#3b5c3a26393c19b400844ac422bd0f631a94d69d" + integrity sha512-aK9jxMypeSrhiYofWWBf/T7O+KwaiAHzM4sveCdWPn71lzUSMimRnKzhXDKfKwV1kWoBo2P1aGgaIYGLf9/ljw== + +-"@types/node@8.0.33": +- version "8.0.33" +- resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.33.tgz#1126e94374014e54478092830704f6ea89df04cd" +- integrity sha512-vmCdO8Bm1ExT+FWfC9sd9r4jwqM7o97gGy2WBshkkXbf/2nLAJQUrZfIhw27yVOtLUev6kSZc4cav/46KbDd8A== ++"@types/node@^10.12.12": ++ version "10.17.15" ++ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.15.tgz#bfff4e23e9e70be6eec450419d51e18de1daf8e7" ++ integrity sha512-daFGV9GSs6USfPgxceDA8nlSe48XrVCJfDeYm7eokxq/ye7iuOH87hKXgMtEAVLFapkczbZsx868PMDT1Y0a6A== + + "@types/puppeteer@^1.19.0": + version "1.19.1" +@@ -1751,10 +1751,10 @@ typedarray@^0.0.6: + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +-typescript@2.9.2: +- version "2.9.2" +- resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c" +- integrity sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w== ++typescript@3.7.2: ++ version "3.7.2" ++ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb" ++ integrity sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ== + + union-value@^1.0.0: + version "1.0.1" +diff --git a/test/smoke/package.json b/test/smoke/package.json +index 2ae2926ada..14b0c621ff 100644 +--- a/test/smoke/package.json ++++ b/test/smoke/package.json +@@ -27,7 +27,7 @@ + "rimraf": "^2.6.1", + "strip-json-comments": "^2.0.1", + "tmp": "0.0.33", +- "typescript": "2.9.2", ++ "typescript": "3.7.2", + "watch": "^1.0.2" + } + } +diff --git a/test/smoke/yarn.lock b/test/smoke/yarn.lock +index 82626a55c7..5d3ee1b69b 100644 +--- a/test/smoke/yarn.lock ++++ b/test/smoke/yarn.lock +@@ -2122,10 +2122,10 @@ tree-kill@^1.1.0: + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.0.tgz#5846786237b4239014f05db156b643212d4c6f36" + integrity sha512-DlX6dR0lOIRDFxI0mjL9IYg6OTncLm/Zt+JiBhE5OlFcAR8yc9S7FFXU9so0oda47frdM/JFsk7UjNt9vscKcg== + +-typescript@2.9.2: +- version "2.9.2" +- resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c" +- integrity sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w== ++typescript@3.7.2: ++ version "3.7.2" ++ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb" ++ integrity sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ== + + union-value@^1.0.0: + version "1.0.1" +diff --git a/yarn.lock b/yarn.lock +index a98533bad9..f4da0987c9 100644 +--- a/yarn.lock ++++ b/yarn.lock +@@ -140,6 +140,23 @@ + lodash "^4.17.13" + to-fast-properties "^2.0.0" + ++"@coder/logger@^1.1.12": ++ version "1.1.12" ++ resolved "https://registry.yarnpkg.com/@coder/logger/-/logger-1.1.12.tgz#def113b7183abc35a8da2b57f0929f7e9626f4e0" ++ integrity sha512-oM0j3lTVPqApUm3e0bKKcXpfAiJEys31fgEfQlHmvEA13ujsC4zDuXnt0uzDtph48eMoNRLOF/EE4mNShVJKVw== ++ ++"@coder/node-browser@^1.0.8": ++ version "1.0.8" ++ resolved "https://registry.yarnpkg.com/@coder/node-browser/-/node-browser-1.0.8.tgz#c22f581b089ad7d95ad1362fd351c57b7fbc6e70" ++ integrity sha512-NLF9sYMRCN9WK1C224pHax1Cay3qKypg25BhVg7VfNbo3Cpa3daata8RF/rT8JK3lPsu8PmFgDRQjzGC9X1Lrw== ++ ++"@coder/requirefs@^1.1.5": ++ version "1.1.5" ++ resolved "https://registry.yarnpkg.com/@coder/requirefs/-/requirefs-1.1.5.tgz#259db370d563a79a96fb150bc9d69c7db6edc9fb" ++ integrity sha512-3jB47OFCql9+9FI6Vc4YX0cfFnG5rxBfrZUH45S4XYtYGOz+/Xl4h4d2iMk50b7veHkeSWGlB4VHC3UZ16zuYQ== ++ optionalDependencies: ++ jszip "2.6.0" ++ + "@istanbuljs/schema@^0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" +@@ -5371,6 +5388,13 @@ jsprim@^1.2.2: + json-schema "0.2.3" + verror "1.10.0" + ++jszip@2.6.0: ++ version "2.6.0" ++ resolved "https://registry.yarnpkg.com/jszip/-/jszip-2.6.0.tgz#7fb3e9c2f11c8a9840612db5dabbc8cf3a7534b7" ++ integrity sha1-f7PpwvEciphAYS212rvIzzp1NLc= ++ dependencies: ++ pako "~1.0.0" ++ + just-debounce@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/just-debounce/-/just-debounce-1.0.0.tgz#87fccfaeffc0b68cd19d55f6722943f929ea35ea" +@@ -6761,6 +6785,11 @@ p-try@^2.0.0: + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1" + integrity sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ== + ++pako@~1.0.0: ++ version "1.0.11" ++ resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" ++ integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== ++ + pako@~1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" diff --git a/ci/vscode.sh b/ci/vscode.sh new file mode 100755 index 000000000..c415e3fd1 --- /dev/null +++ b/ci/vscode.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail + +# 1. Ensures VS Code is cloned. +# 2. Patches it. +# 3. Installs it. +main() { + cd "$(dirname "$0")/.." + + git submodule update --init + + # If the patch fails to apply, then it's likely already applied + yarn vscode:patch &> /dev/null || true + + ( + cd lib/vscode + # Install VS Code dependencies. + yarn + + # NODE_MODULE_VERSION mismatch errors without this. + npm rebuild + ) +} + +main "$@" diff --git a/doc/CONTRIBUTING.md b/doc/CONTRIBUTING.md new file mode 100644 index 000000000..935994f37 --- /dev/null +++ b/doc/CONTRIBUTING.md @@ -0,0 +1,30 @@ +# Contributing + +## Development Workflow + +- [VS Code prerequisites](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#prerequisites) + +```shell +yarn +yarn vscode +yarn watch # Visit http://localhost:8080 once completed. +``` + +Any changes made to the source will be live reloaded. + +If changes are made to the patch and you've built previously you must manually +reset VS Code then run `yarn vscode:patch`. + +Some docs are available at [../src/node/app](../src/node/app) on how code-server +works internally. + +## Build + +- [VS Code prerequisites](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#prerequisites) + +```shell +yarn +yarn vscode +yarn build +node ./build/out/entry.js # Run the built JavaScript with Node. +``` diff --git a/doc/FAQ.md b/doc/FAQ.md new file mode 100644 index 000000000..bf0dde162 --- /dev/null +++ b/doc/FAQ.md @@ -0,0 +1,110 @@ +# FAQ + +## Questions? + +Please file all questions and support requests at https://www.reddit.com/r/codeserver/ +The issue tracker is only for bugs. + +## What's the deal with extensions? + +Unfortunately, the Microsoft VS Code Marketplace license prohibits use with any non Microsoft +product. + +See https://cdn.vsassets.io/v/M146_20190123.39/_content/Microsoft-Visual-Studio-Marketplace-Terms-of-Use.pdf + +> Marketplace Offerings are intended for use only with Visual Studio Products and Services +> and you may only install and use Marketplace Offerings with Visual Studio Products and Services. + +As a result, Coder has created its own marketplace for open source extensions. It works by scraping +GitHub for VS Code extensions and building them. It's not perfect but getting better by the day with +more and more extensions. + +Issue [https://github.com/cdr/code-server/issues/1299](#1299) is a big one in making the experience here +better by allowing the community to submit extensions and repos to avoid waiting until the scraper finds +an extension. + +If an extension does not work, try to grab its VSIX from its Github releases or build it yourself and +copy it to the extensions folder. + +## How is this different from VS Code Online? + +VS Code Online is a closed source managed service by Microsoft and only runs on Azure. + +code-server is open source and can be freely ran on any machine. + +## How should I expose code-server to the internet? + +By far the most secure method of using code-server is via +[sshcode](https://github.com/codercom/sshcode) as it runs code-server and then forwards +its port over SSH and requires no setup on your part other than having a working SSH server. + +You can also forward your SSH key and GPG agent to the remote machine to securely access GitHub +and securely sign commits without duplicating your keys onto the the remote machine. + +1. https://developer.github.com/v3/guides/using-ssh-agent-forwarding/ +1. https://wiki.gnupg.org/AgentForwarding + +If you cannot use sshcode, then you will need to ensure there is some sort of authorization in +front of code-server and that you are using HTTPS to secure all connections. + +By default when listening externally, code-server enables password authentication using a +randomly generated password so you can use that. You can set the `PASSWORD` environment variable +to use your own instead. If you want to handle authentication yourself, use `--auth none` +to disable password authentication. + +If you want to use external authentication you should handle this with a reverse +proxy using something like [oauth2_proxy](https://github.com/pusher/oauth2_proxy). + +For HTTPS, you can use a self signed certificate by passing in just `--cert` or pass in an existing +certificate by providing the path to `--cert` and the path to its key with `--cert-key`. + +If `code-server` has been passed a certificate it will also respond to HTTPS +requests and will redirect all HTTP requests to HTTPS. Otherwise it will respond +only to HTTP requests. + +You can use [Let's Encrypt](https://letsencrypt.org/) to get an SSL certificate +for free. + +## x86 releases? + +node has dropped support for x86 and so we decided to as well. See +[nodejs/build/issues/885](https://github.com/nodejs/build/issues/885). + +## Alpine builds? + +Just install `libc-dev` and code-server should work. + +## Multi Tenancy + +If you want to run multiple code-server's on shared infrastructure, we recommend using virtual +machines with a VM per user. This will easily allow users to run a docker daemon. If you want +to use kubernetes, you'll definitely want to use [kubevirt](https://kubevirt.io) to give each +user a virtual machine instead of just a container. Docker in docker while supported requires +privileged containers which are a security risk in a multi tenant infrastructure. + +## Docker in code-server docker container? + +If you'd like to access docker inside of code-server, we'd recommend running a docker:dind container +and mounting in a directory to share between dind and the code-server container at /var/run. After, install +the docker CLI in the code-server container and you should be able to access the daemon as the socket +will be shared at /var/run/docker.sock. + +In order to make volume mounts work, mount the home directory in the code-server container and the +dind container at the same path. i.e you'd volume mount a directory from the host to `/home/coder` +on both. This will allow any volume mounts in the home directory to work. Similar process +to make volume mounts in any other directory work. + +## Collaboration + +At the moment we have no plans for multi user collaboration on code-server but we understand there is strong +demand and will work on it when the time is right. + +## How can I disable telemetry? + +Use the `--disable-telemetry` flag to completely disable telemetry. We use the +data collected only to improve code-server. + +## Enterprise + +Visit [our enterprise page](https://coder.com) for more information about our +enterprise offerings. diff --git a/doc/assets/code-server.gif b/doc/assets/code-server.gif new file mode 100644 index 000000000..9f6ec6e1a Binary files /dev/null and b/doc/assets/code-server.gif differ diff --git a/doc/assets/cros.png b/doc/assets/cros.png deleted file mode 100644 index 64d94c230..000000000 Binary files a/doc/assets/cros.png and /dev/null differ diff --git a/doc/assets/ide.gif b/doc/assets/ide.gif deleted file mode 100644 index c8183c1e9..000000000 Binary files a/doc/assets/ide.gif and /dev/null differ diff --git a/doc/assets/release.gif b/doc/assets/release.gif deleted file mode 100644 index b8ad49973..000000000 Binary files a/doc/assets/release.gif and /dev/null differ diff --git a/doc/cros-install.md b/doc/cros-install.md deleted file mode 100644 index 0c12b6db5..000000000 --- a/doc/cros-install.md +++ /dev/null @@ -1,75 +0,0 @@ -# Installing code-server in your ChromiumOS/ChromeOS/CloudReady machine - -This guide will show you how to install code-server into your CrOS machine. - -## Using Crostini - -One of the easier ways to run code-server is via -[Crostini](https://www.aboutchromebooks.com/tag/project-crostini/), the Linux -apps support feature in CrOS. Make sure you have enough RAM, HDD space and your -CPU has VT-x/ AMD-V support. If your chromebook has this, then you are -qualified to use Crostini. - -If you are running R69, you might want to enable this on -[Chrome Flags](chrome://flags/#enable-experimental-crostini-ui). -If you run R72, however, this is already enabled for you. - -After checking your prerequisites, follow the steps in [the self-host install guide](index.md) -on installing code-server. Once done, make sure code-server works by running -it. After running it, simply go to `penguin.linux.test:8080` to access -code-server. Now you should be greeted with this screen. If you did, -congratulations, you have installed code-server in your Chromebook! - -![code-server on Chromebook](assets/cros.png) - -Alternatively, if you ran code-server in another container and you need the IP -for that specific container, simply go to Termina's shell via `crosh` and type -`vsh termina`. - -```bash -Loading extra module: /usr/share/crosh/dev.d/50-crosh.sh -Welcome to crosh, the Chrome OS developer shell. - -If you got here by mistake, don't panic! Just close this tab and carry on. - -Type 'help' for a list of commands. - -If you want to customize the look/behavior, you can use the options page. -Load it by using the Ctrl+Shift+P keyboard shortcut. - -crosh> vsh termina -(termina) chronos@localhost ~ $ -``` -While in termina, run `lxc list`. It should output the list of running containers. - -```bash -(termina) chronos@localhost ~ $ lxc list -+---------|---------|-----------------------|------|------------|-----------+ -| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | -+---------|---------|-----------------------|------|------------|-----------+ -| penguin | RUNNING | 100.115.92.199 (eth0) | | PERSISTENT | 0 | -+---------|---------|-----------------------|------|------------|-----------+ -(termina) chronos@localhost ~ $ -``` - -For this example, we show the default `penguin` container, which is exposed on -`eth0` at 100.115.92.199. Simply enter the IP of the container where the -code-server runs to Chrome. - -## Using Crouton - -[Crouton](https://github.com/dnschneid/crouton) is one of the old ways to get a -running full Linux via `chroot` on a Chromebook. To use crouton, enable -developer mode and go to `crosh`. This time, run `shell`, which should drop you -to `bash`. - -Make sure you downloaded `crouton`, if so, go ahead and run it under -`~/Downloads`. After installing your chroot container via crouton, go ahead and -enter `enter-chroot` to enter your container. - -Follow the instructions set in [the self-host install guide](index.md) to -install code-server. After that is done, run `code-server` and verify it works -by going to `localhost:8080`. - -> At this point in writing, `localhost` seems to work in this method. However, -> the author is not sure if it applies still to newer Chromebooks. diff --git a/doc/deploy.md b/doc/deploy.md deleted file mode 100644 index f2505e507..000000000 --- a/doc/deploy.md +++ /dev/null @@ -1,73 +0,0 @@ -# Set up instance -## EC2 on AWS -- Click **Launch Instance** from your [EC2 dashboard](https://console.aws.amazon.com/ec2/v2/home). -- Select the Ubuntu Server 18.04 LTS (HVM), SSD Volume Type -- Select an appropriate instance size (we recommend t2.medium/large, depending - on team size and number of repositories/languages enabled), then - **Next: Configure Instance Details**. -- Select **Next: ...** until you get to the **Configure Security Group** page, - then add a **Custom TCP Rule** rule with port range set to `8080` and source - set to "Anywhere". - > Rules with source of 0.0.0.0/0 allow all IP addresses to access your - > instance. We recommend setting [security group rules](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-network-security.html?icmpid=docs_ec2_console) - > to allow access from known IP addresses only. -- Click **Launch**. -- You will be prompted to create a key pair. -- From the dropdown choose "create a new pair", give the key pair a name. -- Click **Download Key Pair** and store the file in a safe place. -- Click **Launch Instances**. -- Head to your [EC2 dashboard](https://console.aws.amazon.com/ec2/v2/home) - and choose instances from the left panel. -- In the description of your EC2 instance copy the public DNS (iPv4) address - using the copy to clipboard button. -- Open a terminal on your computer and SSH into your instance: - ``` - ssh -i ${path to key pair} ubuntu@${public address} - ``` - -## DigitalOcean -[Open your DigitalOcean dashboard](https://cloud.digitalocean.com/droplets/new) -to create a new droplet - -- **Choose an image -** Select the **Distributions** tab and then choose Ubuntu. -- **Choose a size -** We recommend at least 4GB RAM and 2 CPU, more depending - on team size and number of repositories/languages enabled. -- Launch your instance. -- Open a terminal on your computer and SSH into your instance: - ``` - ssh root@${instance ip} - ``` - -## Google Cloud -> Pre-requisite: Set up the [Google Cloud SDK](https://cloud.google.com/sdk/docs/) -> on your local machine - -- [Open your Google Cloud console](https://console.cloud.google.com/compute/instances) - to create a new VM instance and click **Create Instance**. -- Choose an appropriate machine type (we recommend 2 vCPU and 7.5 GB RAM, more - depending on team size and number of repositories/languages enabled). -- Choose Ubuntu 16.04 LTS as your boot disk. -- Expand the "Management, security, disks, networking, sole tenancy" section, - go to the "Networking" tab, then under network tags add "code-server". -- Create your VM, and **take note** of its public IP address. -- Visit "VPC network" in the console and go to "Firewall rules". Create a new - firewall rule called "http-8080". Under "Target tags" add "code-server", and - under "Protocols and ports" tick "Specified protocols and ports" and "tcp". - Beside "tcp", add "8080", then create the rule. -- Open a terminal on your computer and SSH into your Google Cloud VM: - ``` - gcloud compute ssh --zone ${region} ${instance name} - ``` -# Run code-server -- Download the latest code-server release from the - [releases page](https://github.com/cdr/code-server/releases/latest) - to the instance, extract the file, then run the code-server binary: - ``` - wget https://github.com/cdr/code-server/releases/download/{version}/code-server{version}-linux-x64.tar.gz - tar -xvzf code-server{version}-linux-x64.tar.gz - cd code-server{version}-linux-x64 - ./code-server - ``` -- Open your browser and visit http://$public_ip:8080/ where `$public_ip` is - your instance's public IP address. -- For long-term use, set up a systemd service to run code-server. diff --git a/doc/examples/fail2ban.conf b/doc/examples/fail2ban.conf deleted file mode 100644 index 7968ce0ba..000000000 --- a/doc/examples/fail2ban.conf +++ /dev/null @@ -1,15 +0,0 @@ -# Fail2Ban filter for code-server - -[Definition] - -failregex = ^Failed login attempt\s+{\"remoteAddress\":\"\" - -# Use this instead for proxies (ensure the proxy is configured to send the -# X-Forwarded-For header). -# failregex = ^Failed login attempt\s+{\"xForwardedFor\":\"\" - -ignoreregex = - -datepattern = "timestamp":{EPOCH}}$ - -# Author: Dean Sheather diff --git a/doc/examples/kubernetes.aws.yaml b/doc/examples/kubernetes.aws.yaml deleted file mode 100644 index 079da4b47..000000000 --- a/doc/examples/kubernetes.aws.yaml +++ /dev/null @@ -1,73 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: code-server ---- -apiVersion: v1 -kind: Service -metadata: - name: code-server - namespace: code-server -spec: - ports: - - port: 8080 - name: https - protocol: TCP - selector: - app: code-server - type: ClusterIP ---- -kind: StorageClass -apiVersion: storage.k8s.io/v1 -metadata: - name: gp2 - annotations: - storageclass.kubernetes.io/is-default-class: "true" -provisioner: kubernetes.io/aws-ebs -parameters: - type: gp2 - fsType: ext4 ---- -kind: PersistentVolumeClaim -apiVersion: v1 -metadata: - name: code-store - namespace: code-server -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 60Gi ---- -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - labels: - app: code-server - name: code-server - namespace: code-server -spec: - selector: - matchLabels: - app: code-server - replicas: 1 - template: - metadata: - labels: - app: code-server - spec: - containers: - - image: codercom/code-server:v2 - imagePullPolicy: Always - name: code-servery - ports: - - containerPort: 8080 - name: https - volumeMounts: - - name: code-server-storage - mountPath: /go/src - volumes: - - name: code-server-storage - persistentVolumeClaim: - claimName: code-store diff --git a/doc/examples/kubernetes.yaml b/doc/examples/kubernetes.yaml deleted file mode 100644 index 6259689d3..000000000 --- a/doc/examples/kubernetes.yaml +++ /dev/null @@ -1,43 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: code-server ---- -apiVersion: v1 -kind: Service -metadata: - name: code-server - namespace: code-server -spec: - ports: - - port: 8080 - name: https - protocol: TCP - selector: - app: code-server - type: ClusterIP ---- -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - labels: - app: code-server - name: code-server - namespace: code-server -spec: - selector: - matchLabels: - app: code-server - replicas: 1 - template: - metadata: - labels: - app: code-server - spec: - containers: - - image: codercom/code-server:v2 - imagePullPolicy: Always - name: code-server - ports: - - containerPort: 8080 - name: https diff --git a/doc/fail2ban.md b/doc/fail2ban.md deleted file mode 100644 index ce4b7179e..000000000 --- a/doc/fail2ban.md +++ /dev/null @@ -1,35 +0,0 @@ -# Protecting code-server from bruteforce attempts -code-server outputs all failed login attempts, along with the IP address, -provided password, user agent and timestamp by default. - -When using a reverse proxy such as Nginx or Apache, the remote address may -appear to be `127.0.0.1` or a similar address so `X-Forwarded-For` should be -used instead. Ensure that you are setting this value in your reverse proxy: - -Nginx: -``` -location / { - ... - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - ... -} -``` - -Apache: -``` - - ... - SetEnvIf X-Forwarded-For "^.*\..*\..*\..*" forwarded - ... - -``` - -It is extremely important that you ensure that your code-server instance is not -accessible from the internet (use localhost or block it in your firewall). - -## Fail2Ban -Fail2Ban allows for automatically banning and logging repeated failed -authentication attempts for many applications through regex filters. A working -filter for code-server can be found in `./examples/fail2ban.conf`. Once this -is installed and configured correctly, repeated failed login attempts should -automatically be banned from connecting to your server. diff --git a/doc/quickstart.md b/doc/quickstart.md deleted file mode 100644 index 318bbebae..000000000 --- a/doc/quickstart.md +++ /dev/null @@ -1,98 +0,0 @@ -# Quickstart Guide -1. Visit the [releases page](https://github.com/cdr/code-server/releases) and - download the latest binary for your operating system. -2. Unpack the downloaded file then run the binary. -3. In your browser navigate to `localhost:8080`. - -## Usage -Run `code-server --help` to view available options. - -### Nginx Reverse Proxy -The trailing slashes are important. - -``` -server { - listen 80; - listen [::]:80; - server_name code.example.com code.example.org; - location /some/path/ { # Or / if hosting at the root. - proxy_pass http://localhost:8080/; - proxy_set_header Host $host; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection upgrade; - proxy_set_header Accept-Encoding gzip; - } -} -``` - -### Apache Reverse Proxy -``` - - ServerName code.example.com - - RewriteEngine On - RewriteCond %{HTTP:Upgrade} =websocket [NC] - RewriteRule /(.*) ws://localhost:8080/$1 [P,L] - RewriteCond %{HTTP:Upgrade} !=websocket [NC] - RewriteRule /(.*) http://localhost:8080/$1 [P,L] - - ProxyRequests off - ProxyPass / http://localhost:8080/ nocanon - ProxyPassReverse / http://localhost:8080/ - -``` - -### Run automatically at startup - -In some cases you might need to run code-server automatically once the host starts. You may use your local init service to do so. - -#### Systemd - -```ini -[Unit] -Description=Code Server IDE -After=network.target - -[Service] -Type=simple -User= -EnvironmentFile=$HOME/.profile -WorkingDirectory=$HOME -Restart=on-failure -RestartSec=10 - -ExecStart= $(pwd) - -StandardOutput=file:/var/log/code-server-output.log -StandardError=file:/var/log/code-server-error.log - -[Install] -WantedBy=multi-user.target -``` - -#### OpenRC - -```sh -#!/sbin/openrc-run - -depend() { - after net-online - need net -} - -supervisor=supervise-daemon -name="code-server" -command="/opt/cdr/code-server" -command_args="" - -pidfile="/var/run/cdr.pid" -respawn_delay=5 - -set -o allexport -if [ -f /etc/environment ]; then source /etc/environment; fi -set +o allexport -``` - -#### Kubernetes/Docker - -Make sure you set your restart policy to always - this will ensure your container starts as the daemon starts. diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 203f96f6d..000000000 --- a/docker-compose.yml +++ /dev/null @@ -1,13 +0,0 @@ -version: "3" - -services: - code-server: - container_name: code-server - image: codercom/code-server - ports: - - "8080:8080" - volumes: - - "${PWD}:/home/coder/project" - - "${HOME}/.local/share/code-server:/home/coder/.local/share/code-server" - environment: - PASSWORD: ${PASSWORD} diff --git a/lib/vscode b/lib/vscode new file mode 160000 index 000000000..ae08d5460 --- /dev/null +++ b/lib/vscode @@ -0,0 +1 @@ +Subproject commit ae08d5460b5a45169385ff3fd44208f431992451 diff --git a/main.js b/main.js deleted file mode 100644 index 360cd6eaa..000000000 --- a/main.js +++ /dev/null @@ -1,7 +0,0 @@ -// Once our entry file is loaded we no longer need nbin to bypass normal Node -// execution. We can still shim the fs into the binary even when bypassing. This -// will ensure for example that a spawn like `${process.argv[0]} -e` will work -// while still allowing us to access files within the binary. -process.env.NBIN_BYPASS = true; - -require("../../bootstrap-amd").load("vs/server/src/node/cli"); diff --git a/package.json b/package.json index 12dcf927d..df7cd5457 100644 --- a/package.json +++ b/package.json @@ -1,42 +1,60 @@ { + "name": "code-server", "license": "MIT", + "version": "3.0.0", "scripts": { - "runner": "cd ./scripts && node --max-old-space-size=32384 -r ts-node/register ./build.ts", - "start": "nodemon --watch ../../../out --verbose ../../../out/vs/server/main.js", - "test": "./scripts/test.sh", - "watch": "cd ../../../ && yarn watch", - "build": "yarn && yarn runner build", - "package": "yarn runner package", - "binary": "yarn runner binary", - "patch:generate": "cd ../../../ && git diff --staged > ./src/vs/server/scripts/vscode.patch", - "patch:apply": "cd ../../../ && git apply ./src/vs/server/scripts/vscode.patch" + "clean": "ci/clean.sh", + "vscode": "ci/vscode.sh", + "vscode:patch": "cd ./lib/vscode && git apply ../../ci/vscode.patch", + "vscode:diff": "cd ./lib/vscode && git diff HEAD > ../../ci/vscode.patch", + "test": "mocha -r ts-node/register ./test/*.test.ts", + "lint": "ci/lint.sh", + "fmt": "ci/fmt.sh", + "runner": "cd ./ci && NODE_OPTIONS=--max_old_space_size=32384 ts-node ./build.ts", + "build": "yarn runner build", + "watch": "yarn runner watch" }, "devDependencies": { - "@coder/nbin": "^1.2.7", + "@types/adm-zip": "^0.4.32", "@types/fs-extra": "^8.0.1", - "@types/node": "^10.12.12", + "@types/mocha": "^5.2.7", + "@types/node": "^12.12.7", + "@types/parcel-bundler": "^1.12.1", "@types/pem": "^1.9.5", "@types/safe-compare": "^1.1.0", - "@types/tar-fs": "^1.16.1", - "@types/tar-stream": "^1.6.1", - "fs-extra": "^8.1.0", - "nodemon": "^1.19.1", + "@types/semver": "^7.1.0", + "@types/tar-fs": "^1.16.2", + "@types/ws": "^6.0.4", + "@typescript-eslint/eslint-plugin": "^2.0.0", + "@typescript-eslint/parser": "^2.0.0", + "eslint": "^6.2.0", + "eslint-config-prettier": "^6.0.0", + "eslint-plugin-import": "^2.18.2", + "eslint-plugin-prettier": "^3.1.0", + "leaked-handles": "^5.2.0", + "mocha": "^6.2.0", + "parcel-bundler": "^1.12.4", + "prettier": "^1.18.2", + "stylelint": "^13.0.0", + "stylelint-config-recommended": "^3.0.0", "ts-node": "^8.4.1", - "typescript": "3.6" + "typescript": "3.7.2" }, "resolutions": { - "@types/node": "^10.12.12", - "safe-buffer": "^5.1.1" + "@types/node": "^12.12.7", + "safe-buffer": "^5.1.1", + "vfile-message": "^2.0.2" }, "dependencies": { - "@coder/logger": "^1.1.12", - "@coder/node-browser": "^1.0.6", - "@coder/requirefs": "^1.0.6", + "@coder/logger": "1.1.11", + "adm-zip": "^0.4.14", + "fs-extra": "^8.1.0", "httpolyglot": "^0.1.2", "pem": "^1.14.2", "safe-compare": "^1.1.4", + "semver": "^7.1.3", + "tar": "^6.0.1", "tar-fs": "^2.0.0", - "tar-stream": "^2.1.0", - "util": "^0.12.1" + "ws": "^7.2.0" } } diff --git a/scripts/build.ts b/scripts/build.ts deleted file mode 100644 index 945c9b22e..000000000 --- a/scripts/build.ts +++ /dev/null @@ -1,391 +0,0 @@ -import { Binary } from "@coder/nbin"; -import * as cp from "child_process"; -// import * as crypto from "crypto"; -import * as fs from "fs-extra"; -import * as os from "os"; -import * as path from "path"; -import * as util from "util"; - -enum Task { - /** - * Use before running anything that only works inside VS Code. - */ - EnsureInVscode = "ensure-in-vscode", - Binary = "binary", - Package = "package", - Build = "build", -} - -class Builder { - private readonly rootPath = path.resolve(__dirname, ".."); - private readonly outPath = process.env.OUT || this.rootPath; - private _target?: "darwin" | "alpine" | "linux"; - private currentTask?: Task; - - public run(task: Task | undefined, args: string[]): void { - this.currentTask = task; - this.doRun(task, args).catch((error) => { - console.error(error.message); - process.exit(1); - }); - } - - private async task(message: string, fn: () => Promise): Promise { - const time = Date.now(); - this.log(`${message}...`, true); - try { - const t = await fn(); - process.stdout.write(`took ${Date.now() - time}ms\n`); - return t; - } catch (error) { - process.stdout.write("failed\n"); - throw error; - } - } - - /** - * Writes to stdout with an optional newline. - */ - private log(message: string, skipNewline: boolean = false): void { - process.stdout.write(`[${this.currentTask || "default"}] ${message}`); - if (!skipNewline) { - process.stdout.write("\n"); - } - } - - private async doRun(task: Task | undefined, args: string[]): Promise { - if (!task) { - throw new Error("No task provided"); - } - - if (task === Task.EnsureInVscode) { - return process.exit(this.isInVscode(this.rootPath) ? 0 : 1); - } - - // If we're inside VS Code assume we want to develop. In that case we should - // set an OUT directory and not build in this directory, otherwise when you - // build/watch VS Code the build directory will be included. - if (this.isInVscode(this.outPath)) { - throw new Error("Should not build inside VS Code; set the OUT environment variable"); - } - - this.ensureArgument("rootPath", this.rootPath); - this.ensureArgument("outPath", this.outPath); - - const arch = this.ensureArgument("arch", os.arch().replace(/^x/, "x86_")); - const target = this.ensureArgument("target", await this.target()); - const vscodeVersion = this.ensureArgument("vscodeVersion", args[0]); - const codeServerVersion = this.ensureArgument("codeServerVersion", args[1]); - - const vscodeSourcePath = path.join(this.outPath, "source", `vscode-${vscodeVersion}-source`); - const binariesPath = path.join(this.outPath, "binaries"); - const binaryName = `code-server${codeServerVersion}-vsc${vscodeVersion}-${target}-${arch}`; - const finalBuildPath = path.join(this.outPath, "build", `${binaryName}-built`); - - switch (task) { - case Task.Binary: - return this.binary(finalBuildPath, binariesPath, binaryName); - case Task.Package: - return this.package(vscodeSourcePath, binariesPath, binaryName); - case Task.Build: - return this.build(vscodeSourcePath, vscodeVersion, codeServerVersion, finalBuildPath); - default: - throw new Error(`No task matching "${task}"`); - } - } - - /** - * Get the target of the system. - */ - private async target(): Promise<"darwin" | "alpine" | "linux"> { - if (!this._target) { - if (os.platform() === "darwin" || (process.env.OSTYPE && /^darwin/.test(process.env.OSTYPE))) { - this._target = "darwin"; - } else { - // Alpine's ldd doesn't have a version flag but if you use an invalid flag - // (like --version) it outputs the version to stderr and exits with 1. - const result = await util.promisify(cp.exec)("ldd --version") - .catch((error) => ({ stderr: error.message, stdout: "" })); - if (/musl/.test(result.stderr) || /musl/.test(result.stdout)) { - this._target = "alpine"; - } else { - this._target = "linux"; - } - } - } - return this._target; - } - - /** - * Make sure the argument is set. Display the value if it is. - */ - private ensureArgument(name: string, arg?: string): string { - if (!arg) { - this.log(`${name} is missing`); - throw new Error("Usage: "); - } - this.log(`${name} is "${arg}"`); - return arg; - } - - /** - * Return true if it looks like we're inside VS Code. This is used to prevent - * accidentally building inside VS Code while developing which causes issues - * because the watcher will try compiling those built files. - */ - private isInVscode(pathToCheck: string): boolean { - let inside = false; - const maybeVsCode = path.join(pathToCheck, "../../../"); - try { - // If it has a package.json with the right name it's probably VS Code. - inside = require(path.join(maybeVsCode, "package.json")).name === "code-oss-dev"; - } catch (error) {} - this.log( - inside - ? `Running inside VS Code ([${maybeVsCode}]${path.relative(maybeVsCode, pathToCheck)})` - : "Not running inside VS Code" - ); - return inside; - } - - /** - * Build code-server within VS Code. - */ - private async build(vscodeSourcePath: string, vscodeVersion: string, codeServerVersion: string, finalBuildPath: string): Promise { - // Install dependencies (should be cached by CI). - await this.task("Installing code-server dependencies", async () => { - await util.promisify(cp.exec)("yarn", { cwd: this.rootPath }); - }); - - // Download and prepare VS Code if necessary (should be cached by CI). - if (fs.existsSync(vscodeSourcePath)) { - this.log("Using existing VS Code clone"); - } else { - await this.task("Cloning VS Code", () => { - return util.promisify(cp.exec)( - "git clone https://github.com/microsoft/vscode" - + ` --quiet --branch "${vscodeVersion}"` - + ` --single-branch --depth=1 "${vscodeSourcePath}"`); - }); - } - - await this.task("Installing VS Code dependencies", () => { - return util.promisify(cp.exec)("yarn", { cwd: vscodeSourcePath }); - }); - - if (fs.existsSync(path.join(vscodeSourcePath, ".build/extensions"))) { - this.log("Using existing built-in-extensions"); - } else { - await this.task("Building default extensions", () => { - return util.promisify(cp.exec)( - "yarn gulp compile-extensions-build --max-old-space-size=32384", - { cwd: vscodeSourcePath }, - ); - }); - } - - // Clean before patching or it could fail if already patched. - await this.task("Patching VS Code", async () => { - await util.promisify(cp.exec)("git reset --hard", { cwd: vscodeSourcePath }); - await util.promisify(cp.exec)("git clean -fd", { cwd: vscodeSourcePath }); - await util.promisify(cp.exec)(`git apply ${this.rootPath}/scripts/vscode.patch`, { cwd: vscodeSourcePath }); - }); - - const serverPath = path.join(vscodeSourcePath, "src/vs/server"); - await this.task("Copying code-server into VS Code", async () => { - await fs.remove(serverPath); - await fs.mkdirp(serverPath); - await Promise.all(["main.js", "node_modules", "src", "typings"].map((fileName) => { - return fs.copy(path.join(this.rootPath, fileName), path.join(serverPath, fileName)); - })); - }); - - await this.task("Building VS Code", () => { - return util.promisify(cp.exec)("yarn gulp compile-build --max-old-space-size=32384", { cwd: vscodeSourcePath }); - }); - - await this.task("Optimizing VS Code", async () => { - await fs.copyFile(path.join(this.rootPath, "scripts/optimize.js"), path.join(vscodeSourcePath, "coder.js")); - await util.promisify(cp.exec)(`yarn gulp optimize --max-old-space-size=32384 --gulpfile ./coder.js`, { cwd: vscodeSourcePath }); - }); - - const { productJson, packageJson } = await this.task("Generating final package.json and product.json", async () => { - const merge = async (name: string, extraJson: { [key: string]: string } = {}): Promise<{ [key: string]: string }> => { - const [aJson, bJson] = (await Promise.all([ - fs.readFile(path.join(vscodeSourcePath, `${name}.json`), "utf8"), - fs.readFile(path.join(this.rootPath, `scripts/${name}.json`), "utf8"), - ])).map((raw) => { - const json = JSON.parse(raw); - delete json.scripts; - delete json.dependencies; - delete json.devDependencies; - delete json.optionalDependencies; - return json; - }); - - return { ...aJson, ...bJson, ...extraJson }; - }; - - const date = new Date().toISOString(); - const commit = require(path.join(vscodeSourcePath, "build/lib/util")).getVersion(this.rootPath); - - const [productJson, packageJson] = await Promise.all([ - merge("product", { commit, date }), - merge("package", { codeServerVersion: `${codeServerVersion}-vsc${vscodeVersion}` }), - ]); - - // We could do this before the optimization but then it'd be copied into - // three files and unused in two which seems like a waste of bytes. - const apiPath = path.join(vscodeSourcePath, "out-vscode/vs/workbench/workbench.web.api.js"); - await fs.writeFile(apiPath, (await fs.readFile(apiPath, "utf8")).replace('{ /*BUILD->INSERT_PRODUCT_CONFIGURATION*/}', JSON.stringify({ - version: packageJson.version, - codeServerVersion: packageJson.codeServerVersion, - ...productJson, - }))); - - return { productJson, packageJson }; - }); - - if (process.env.MINIFY) { - await this.task("Minifying VS Code", () => { - return util.promisify(cp.exec)("yarn gulp minify --max-old-space-size=32384 --gulpfile ./coder.js", { cwd: vscodeSourcePath }); - }); - } - - const finalServerPath = path.join(finalBuildPath, "out/vs/server"); - await this.task("Copying into final build directory", async () => { - await fs.remove(finalBuildPath); - await fs.mkdirp(finalBuildPath); - await Promise.all([ - fs.copy(path.join(vscodeSourcePath, "remote/node_modules"), path.join(finalBuildPath, "node_modules")), - fs.copy(path.join(vscodeSourcePath, ".build/extensions"), path.join(finalBuildPath, "extensions")), - fs.copy(path.join(vscodeSourcePath, `out-vscode${process.env.MINIFY ? "-min" : ""}`), path.join(finalBuildPath, "out")).then(() => { - return Promise.all([ - fs.remove(path.join(finalServerPath, "node_modules")).then(() => { - return fs.copy(path.join(serverPath, "node_modules"), path.join(finalServerPath, "node_modules")); - }), - fs.copy(path.join(finalServerPath, "src/browser/workbench-build.html"), path.join(finalServerPath, "src/browser/workbench.html")), - ]); - }), - ]); - }); - - if (process.env.MINIFY) { - await this.task("Restricting to production dependencies", async () => { - await Promise.all(["package.json", "yarn.lock"].map((fileName) => { - Promise.all([ - fs.copy(path.join(this.rootPath, fileName), path.join(finalServerPath, fileName)), - fs.copy(path.join(path.join(vscodeSourcePath, "remote"), fileName), path.join(finalBuildPath, fileName)), - ]); - })); - - await Promise.all([finalServerPath, finalBuildPath].map((cwd) => { - return util.promisify(cp.exec)("yarn --production", { cwd }); - })); - - await Promise.all(["package.json", "yarn.lock"].map((fileName) => { - return Promise.all([ - fs.remove(path.join(finalServerPath, fileName)), - fs.remove(path.join(finalBuildPath, fileName)), - ]); - })); - }); - } - - await this.task("Writing final package.json and product.json", () => { - return Promise.all([ - fs.writeFile(path.join(finalBuildPath, "package.json"), JSON.stringify(packageJson, null, 2)), - fs.writeFile(path.join(finalBuildPath, "product.json"), JSON.stringify(productJson, null, 2)), - ]); - }); - - // Prevent needless cache changes. - await this.task("Cleaning for smaller cache", () => { - return Promise.all([ - fs.remove(serverPath), - fs.remove(path.join(vscodeSourcePath, "out-vscode")), - fs.remove(path.join(vscodeSourcePath, "out-vscode-min")), - fs.remove(path.join(vscodeSourcePath, "out-build")), - util.promisify(cp.exec)("git reset --hard", { cwd: vscodeSourcePath }).then(() => { - return util.promisify(cp.exec)("git clean -fd", { cwd: vscodeSourcePath }); - }), - ]); - }); - - // Prepend code to the target which enables finding files within the binary. - const prependLoader = async (relativeFilePath: string): Promise => { - const filePath = path.join(finalBuildPath, relativeFilePath); - const shim = ` - if (!global.NBIN_LOADED) { - try { - const nbin = require("nbin"); - nbin.shimNativeFs("${finalBuildPath}"); - global.NBIN_LOADED = true; - const path = require("path"); - const rg = require("vscode-ripgrep"); - rg.binaryRgPath = rg.rgPath; - rg.rgPath = path.join(require("os").tmpdir(), "code-server", path.basename(rg.binaryRgPath)); - } catch (error) { /* Not in the binary. */ } - } - `; - await fs.writeFile(filePath, shim + (await fs.readFile(filePath, "utf8"))); - }; - - await this.task("Prepending nbin loader", () => { - return Promise.all([ - prependLoader("out/vs/server/main.js"), - prependLoader("out/bootstrap-fork.js"), - prependLoader("extensions/node_modules/typescript/lib/tsserver.js"), - ]); - }); - - this.log(`Final build: ${finalBuildPath}`); - } - - /** - * Bundles the built code into a binary. - */ - private async binary(targetPath: string, binariesPath: string, binaryName: string): Promise { - const bin = new Binary({ - mainFile: path.join(targetPath, "out/vs/server/main.js"), - target: await this.target(), - }); - - bin.writeFiles(path.join(targetPath, "**")); - - await fs.mkdirp(binariesPath); - - const binaryPath = path.join(binariesPath, binaryName); - await fs.writeFile(binaryPath, await bin.build()); - await fs.chmod(binaryPath, "755"); - - this.log(`Binary: ${binaryPath}`); - } - - /** - * Package the binary into a release archive. - */ - private async package(vscodeSourcePath: string, binariesPath: string, binaryName: string): Promise { - const releasePath = path.join(this.outPath, "release"); - const archivePath = path.join(releasePath, binaryName); - - await fs.remove(archivePath); - await fs.mkdirp(archivePath); - - await fs.copyFile(path.join(binariesPath, binaryName), path.join(archivePath, "code-server")); - await fs.copyFile(path.join(this.rootPath, "README.md"), path.join(archivePath, "README.md")); - await fs.copyFile(path.join(vscodeSourcePath, "LICENSE.txt"), path.join(archivePath, "LICENSE.txt")); - await fs.copyFile(path.join(vscodeSourcePath, "ThirdPartyNotices.txt"), path.join(archivePath, "ThirdPartyNotices.txt")); - - if ((await this.target()) === "darwin") { - await util.promisify(cp.exec)(`zip -r "${binaryName}.zip" "${binaryName}"`, { cwd: releasePath }); - this.log(`Archive: ${archivePath}.zip`); - } else { - await util.promisify(cp.exec)(`tar -czf "${binaryName}.tar.gz" "${binaryName}"`, { cwd: releasePath }); - this.log(`Archive: ${archivePath}.tar.gz`); - } - } -} - -const builder = new Builder(); -builder.run(process.argv[2] as Task, process.argv.slice(3)); diff --git a/scripts/cacher.sh b/scripts/cacher.sh deleted file mode 100755 index 0ee1bc58e..000000000 --- a/scripts/cacher.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env sh -# cacher.sh -- Restore and rebuild cache. -# Cache paths are designed to work with multi-arch builds and are organized -# based on the branch or tag. The master branch cache is used as a fallback. -# This will download and package the cache but it will not upload it. - -set -eu - -# Try restoring from each argument in turn until we get something. -restore() { - for branch in "$@" ; do - if [ -n "$branch" ] ; then - cache_path="https://codesrv-ci.cdr.sh/cache/$branch/$tar.tar.gz" - if wget "$cache_path" ; then - tar xzvf "$tar.tar.gz" - break - fi - fi - done -} - -# We need to cache the built-in extensions and Node modules. Everything inside -# the cache-upload directory will be uploaded as-is to the code-server bucket. -package() { - mkdir -p "cache-upload/cache/$1" - tar czfv "cache-upload/cache/$1/$tar.tar.gz" node_modules source yarn-cache -} - -main() { - cd "$(dirname "$0")/.." - - # Get the branch for this build. - branch=${DRONE_BRANCH:-${DRONE_SOURCE_BRANCH:-${DRONE_TAG:-}}} - - # The cache will be named based on the arch, platform, and libc. - arch=$DRONE_STAGE_ARCH - platform=${PLATFORM:-linux} - case $DRONE_STAGE_NAME in - *alpine*) libc=musl ;; - * ) libc=glibc ;; - esac - - tar="$platform-$arch-$libc" - - # The action is determined by the name of the step. - case $DRONE_STEP_NAME in - *restore*) restore "$branch" "$DRONE_REPO_BRANCH" ;; - *rebuild*|*package*) package "$branch" ;; - *) exit 1 ;; - esac -} - -main "$@" diff --git a/scripts/ci.bash b/scripts/ci.bash deleted file mode 100755 index 4a0378481..000000000 --- a/scripts/ci.bash +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env bash -# ci.bash -- Build code-server in the CI. - -set -euo pipefail - -function target() { - local os=$(uname | tr '[:upper:]' '[:lower:]') - if [[ "$os" == "linux" ]]; then - # Using the same strategy to detect Alpine as build.ts. - local ldd_output=$(ldd --version 2>&1 || true) - if echo "$ldd_output" | grep -iq musl; then - os="alpine" - fi - fi - - echo "${os}-$(uname -m)" -} - -function main() { - cd "$(dirname "${0}")/.." - - # Get the version information. If a specific version wasn't set, generate it - # from the tag and VS Code version. - local vscode_version=${VSCODE_VERSION:-1.42.0} - local code_server_version=${VERSION:-${TRAVIS_TAG:-${DRONE_TAG:-daily}}} - - # Remove everything that isn't the current VS Code source for caching - # (otherwise the cache will contain old versions). - if [[ -d "source/vscode-$vscode_version-source" ]] ; then - mv "source/vscode-$vscode_version-source" "vscode-$vscode_version-source" - fi - rm -rf source/vscode-*-source - if [[ -d "vscode-$vscode_version-source" ]] ; then - mv "vscode-$vscode_version-source" "source/vscode-$vscode_version-source" - fi - - YARN_CACHE_FOLDER="$(pwd)/yarn-cache" - export YARN_CACHE_FOLDER - - # Always minify and package on tags since that's when releases are pushed. - if [[ -n ${DRONE_TAG:-} || -n ${TRAVIS_TAG:-} ]] ; then - export MINIFY="true" - export PACKAGE="true" - fi - - function run-yarn() { - yarn "$1" "$vscode_version" "$code_server_version" - } - - run-yarn build - run-yarn binary - if [[ -n ${PACKAGE:-} ]] ; then - run-yarn package - fi - - # In this case provide a plainly named "code-server" binary. - if [[ -n ${BINARY:-} ]] ; then - mv binaries/code-server*-vsc* binaries/code-server - fi - - # Prepare GCS bucket directory on release. - if [[ -n ${DRONE_TAG:-} || -n ${TRAVIS_TAG:-} ]] ; then - local gcp_dir="gcs_bucket/releases/$code_server_version/$(target)" - - mkdir -p "$gcp_dir" - mv binaries/code-server*-vsc* "$gcp_dir" - if [[ "$(target)" == "linux-x86_64" ]] ; then - mv binaries/code-server*-vsc* "gcs_bucket/latest-linux" - fi - fi -} - -main "$@" diff --git a/scripts/ci.dockerfile b/scripts/ci.dockerfile deleted file mode 100644 index dbb146c89..000000000 --- a/scripts/ci.dockerfile +++ /dev/null @@ -1,39 +0,0 @@ -# We deploy with Ubuntu so that devs have a familiar environment. -FROM ubuntu:18.04 - -RUN apt-get update && apt-get install -y \ - openssl \ - net-tools \ - git \ - locales \ - sudo \ - dumb-init \ - vim \ - curl \ - wget - -RUN locale-gen en_US.UTF-8 -# We cannot use update-locale because docker will not use the env variables -# configured in /etc/default/locale so we need to set it manually. -ENV LC_ALL=en_US.UTF-8 \ - SHELL=/bin/bash - -RUN adduser --gecos '' --disabled-password coder && \ - echo "coder ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/nopasswd - -USER coder -# Create first so these directories will be owned by coder instead of root -# (workdir and mounting appear to both default to root). -RUN mkdir -p /home/coder/project -RUN mkdir -p /home/coder/.local/share/code-server - -WORKDIR /home/coder/project - -# This ensures we have a volume mounted even if the user forgot to do bind -# mount. So that they do not lose their data if they delete the container. -VOLUME [ "/home/coder/project" ] - -COPY ./binaries/code-server* /usr/local/bin/code-server -EXPOSE 8080 - -ENTRYPOINT ["dumb-init", "code-server", "--host", "0.0.0.0"] diff --git a/scripts/optimize.js b/scripts/optimize.js deleted file mode 100644 index 5e4ab4204..000000000 --- a/scripts/optimize.js +++ /dev/null @@ -1,71 +0,0 @@ -// This must be ran from VS Code's root. -const gulp = require("gulp"); -const path = require("path"); -const _ = require("underscore"); -const buildfile = require("./src/buildfile"); -const common = require("./build/lib/optimize"); -const util = require("./build/lib/util"); -const deps = require("./build/dependencies"); - -const vscodeEntryPoints = _.flatten([ - buildfile.entrypoint("vs/workbench/workbench.web.api"), - buildfile.entrypoint("vs/server/src/node/cli"), - buildfile.base, - buildfile.workbenchWeb, - buildfile.workerExtensionHost, - buildfile.keyboardMaps, - buildfile.entrypoint('vs/platform/files/node/watcher/unix/watcherApp', ["vs/css", "vs/nls"]), - buildfile.entrypoint('vs/platform/files/node/watcher/nsfw/watcherApp', ["vs/css", "vs/nls"]), - buildfile.entrypoint('vs/workbench/services/extensions/node/extensionHostProcess', ["vs/css", "vs/nls"]), -]); - -const vscodeResources = [ - "out-build/vs/server/main.js", - "out-build/vs/server/src/node/uriTransformer.js", - "!out-build/vs/server/doc/**", - "out-build/vs/server/src/media/*", - "out-build/vs/workbench/services/extensions/worker/extensionHostWorkerMain.js", - "out-build/bootstrap.js", - "out-build/bootstrap-fork.js", - "out-build/bootstrap-amd.js", - "out-build/paths.js", - 'out-build/vs/**/*.{svg,png,html}', - "!out-build/vs/code/browser/workbench/*.html", - '!out-build/vs/code/electron-browser/**', - "out-build/vs/base/common/performance.js", - "out-build/vs/base/node/languagePacks.js", - "out-build/vs/base/browser/ui/octiconLabel/octicons/**", - "out-build/vs/base/browser/ui/codiconLabel/codicon/**", - "out-build/vs/workbench/browser/media/*-theme.css", - "out-build/vs/workbench/contrib/debug/**/*.json", - "out-build/vs/workbench/contrib/externalTerminal/**/*.scpt", - "out-build/vs/workbench/contrib/webview/browser/pre/*.js", - "out-build/vs/**/markdown.css", - "out-build/vs/workbench/contrib/tasks/**/*.json", - "out-build/vs/platform/files/**/*.md", - "!**/test/**" -]; - -const rootPath = __dirname; -const nodeModules = ["electron", "original-fs"] - .concat(_.uniq(deps.getProductionDependencies(rootPath).map((d) => d.name))) - .concat(_.uniq(deps.getProductionDependencies(path.join(rootPath, "src/vs/server")).map((d) => d.name))) - .concat(Object.keys(process.binding("natives")).filter((n) => !/^_|\//.test(n))); - -gulp.task("optimize", gulp.series( - util.rimraf("out-vscode"), - common.optimizeTask({ - src: "out-build", - entryPoints: vscodeEntryPoints, - resources: vscodeResources, - loaderConfig: common.loaderConfig(nodeModules), - out: "out-vscode", - inlineAmdImages: true, - bundleInfo: undefined - }), -)); - -gulp.task("minify", gulp.series( - util.rimraf("out-vscode-min"), - common.minifyTask("out-vscode") -)); diff --git a/scripts/package.json b/scripts/package.json deleted file mode 100644 index 5085fa85b..000000000 --- a/scripts/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "code-server", - "main": "out/vs/server/main", - "desktopName": "code-server-url-handler.desktop" -} diff --git a/scripts/product.json b/scripts/product.json deleted file mode 100644 index dabf03e82..000000000 --- a/scripts/product.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "nameShort": "code-server", - "nameLong": "code-server", - "applicationName": "code-server", - "dataFolderName": ".code-server", - "win32MutexName": "codeserver", - "win32DirName": "Code Server", - "win32NameVersion": "Code Server", - "win32RegValueName": "CodeServer", - "win32AppId": "", - "win32x64AppId": "", - "win32UserAppId": "", - "win32x64UserAppId": "", - "win32AppUserModelId": "CodeServer", - "win32ShellNameShort": "C&ode Server", - "darwinBundleIdentifier": "com.code.server", - "linuxIconName": "com.code.server", - "urlProtocol": "code-server", - "updateUrl": "https://api.github.com/repos/cdr/code-server/releases", - "quality": "latest" -} diff --git a/scripts/test.sh b/scripts/test.sh deleted file mode 100755 index 08f78570f..000000000 --- a/scripts/test.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env sh -# test.sh -- Simple test for CI. -# We'll have more involved tests eventually. This just ensures the binary has -# been built and runs. - -set -eu - -main() { - cd "$(dirname "$0")/.." - - version=$(./binaries/code-server* --version | head -1) - echo "Got '$version' for the version" - case $version in - *-vsc1.42.0) exit 0 ;; - *) exit 1 ;; - esac -} - -main "$@" diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json deleted file mode 100644 index 149a7f36c..000000000 --- a/scripts/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "moduleResolution": "node", - "noImplicitAny": true, - "experimentalDecorators": true, - "noImplicitReturns": true, - "noUnusedLocals": true, - "noImplicitThis": true, - "alwaysStrict": true, - "strictBindCallApply": true, - "strictNullChecks": true, - "forceConsistentCasingInFileNames": true, - "baseUrl": ".", - "target": "esnext" - } -} diff --git a/scripts/vscode.patch b/scripts/vscode.patch deleted file mode 100644 index 1f111ad82..000000000 --- a/scripts/vscode.patch +++ /dev/null @@ -1,996 +0,0 @@ -diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts -index a68e020f9f..b4ee8a5886 100644 ---- a/src/vs/base/common/network.ts -+++ b/src/vs/base/common/network.ts -@@ -88,7 +88,7 @@ class RemoteAuthoritiesImpl { - if (host && host.indexOf(':') !== -1) { - host = `[${host}]`; - } -- const port = this._ports[authority]; -+ // const port = this._ports[authority]; - const connectionToken = this._connectionTokens[authority]; - let query = `path=${encodeURIComponent(uri.path)}`; - if (typeof connectionToken === 'string') { -@@ -96,8 +96,8 @@ class RemoteAuthoritiesImpl { - } - return URI.from({ - scheme: platform.isWeb ? this._preferredWebSchema : Schemas.vscodeRemoteResource, -- authority: `${host}:${port}`, -- path: `/vscode-remote-resource`, -+ authority: window.location.host, -+ path: `${window.location.pathname.replace(/\/+$/, '')}/vscode-remote-resource`, - query - }); - } -diff --git a/src/vs/base/common/platform.ts b/src/vs/base/common/platform.ts -index 5a631e0b39..8a2b1518d6 100644 ---- a/src/vs/base/common/platform.ts -+++ b/src/vs/base/common/platform.ts -@@ -59,6 +59,17 @@ if (typeof navigator === 'object' && !isElectronRenderer) { - _isWeb = true; - _locale = navigator.language; - _language = _locale; -+ // NOTE@coder: make languages work. -+ const el = typeof document !== 'undefined' && document.getElementById('vscode-remote-nls-configuration'); -+ const rawNlsConfig = el && el.getAttribute('data-settings'); -+ if (rawNlsConfig) { -+ try { -+ const nlsConfig: NLSConfig = JSON.parse(rawNlsConfig); -+ _locale = nlsConfig.locale; -+ _translationsConfigFile = nlsConfig._translationsConfigFile; -+ _language = nlsConfig.availableLanguages['*'] || LANGUAGE_DEFAULT; -+ } catch (error) { /* Oh well. */ } -+ } - } else if (typeof process === 'object') { - _isWindows = (process.platform === 'win32'); - _isMacintosh = (process.platform === 'darwin'); -diff --git a/src/vs/base/common/processes.ts b/src/vs/base/common/processes.ts -index c52f7b3774..5a7e7f579e 100644 ---- a/src/vs/base/common/processes.ts -+++ b/src/vs/base/common/processes.ts -@@ -110,7 +110,10 @@ export function sanitizeProcessEnvironment(env: IProcessEnvironment, ...preserve - /^ELECTRON_.+$/, - /^GOOGLE_API_KEY$/, - /^VSCODE_.+$/, -- /^SNAP(|_.*)$/ -+ /^SNAP(|_.*)$/, -+ // NOTE@coder: add our own environment variables. -+ /^NBIN_BYPASS$/, -+ /^LAUNCH_VSCODE$/ - ]; - const envKeys = Object.keys(env); - envKeys -diff --git a/src/vs/base/node/languagePacks.js b/src/vs/base/node/languagePacks.js -index 2c64061da7..c0ef8faedd 100644 ---- a/src/vs/base/node/languagePacks.js -+++ b/src/vs/base/node/languagePacks.js -@@ -128,7 +128,10 @@ function factory(nodeRequire, path, fs, perf) { - function getLanguagePackConfigurations(userDataPath) { - const configFile = path.join(userDataPath, 'languagepacks.json'); - try { -- return nodeRequire(configFile); -+ // NOTE@coder: Swapped require with readFile since require is cached and -+ // we don't restart the server-side portion of code-server when the -+ // language changes. -+ return JSON.parse(fs.readFileSync(configFile, "utf8")); - } catch (err) { - // Do nothing. If we can't read the file we have no - // language pack config. -diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts -index a599f5a7eb..ec7ccd43f8 100644 ---- a/src/vs/code/browser/workbench/workbench.ts -+++ b/src/vs/code/browser/workbench/workbench.ts -@@ -298,35 +298,6 @@ class WorkspaceProvider implements IWorkspaceProvider { - let workspace: IWorkspace; - let payload = Object.create(null); - -- const query = new URL(document.location.href).searchParams; -- query.forEach((value, key) => { -- switch (key) { -- -- // Folder -- case WorkspaceProvider.QUERY_PARAM_FOLDER: -- workspace = { folderUri: URI.parse(value) }; -- foundWorkspace = true; -- break; -- -- // Workspace -- case WorkspaceProvider.QUERY_PARAM_WORKSPACE: -- workspace = { workspaceUri: URI.parse(value) }; -- foundWorkspace = true; -- break; -- -- // Empty -- case WorkspaceProvider.QUERY_PARAM_EMPTY_WINDOW: -- workspace = undefined; -- foundWorkspace = true; -- break; -- -- // Payload -- case WorkspaceProvider.QUERY_PARAM_PAYLOAD: -- payload = JSON.parse(value); -- break; -- } -- }); -- - // If no workspace is provided through the URL, check for config attribute from server - if (!foundWorkspace) { - if (config.folderUri) { -diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts -index abd1e33b18..bf75952ce1 100644 ---- a/src/vs/platform/environment/common/environment.ts -+++ b/src/vs/platform/environment/common/environment.ts -@@ -37,6 +37,8 @@ export interface ParsedArgs { - logExtensionHostCommunication?: boolean; - 'extensions-dir'?: string; - 'builtin-extensions-dir'?: string; -+ 'extra-extensions-dir'?: string[]; -+ 'extra-builtin-extensions-dir'?: string[]; - extensionDevelopmentPath?: string[]; // // undefined or array of 1 or more local paths or URIs - extensionTestsPath?: string; // either a local path or a URI - 'extension-development-confirm-save'?: boolean; -@@ -147,6 +149,8 @@ export interface IEnvironmentService extends IUserHomeProvider { - disableExtensions: boolean | string[]; - builtinExtensionsPath: string; - extensionsPath?: string; -+ extraExtensionPaths: string[]; -+ extraBuiltinExtensionPaths: string[]; - extensionDevelopmentLocationURI?: URI[]; - extensionTestsLocationURI?: URI; - logExtensionHostCommunication?: boolean; -diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts -index e68e0647c3..49a5aae2fa 100644 ---- a/src/vs/platform/environment/node/argv.ts -+++ b/src/vs/platform/environment/node/argv.ts -@@ -55,6 +55,8 @@ export const OPTIONS: OptionDescriptions> = { - - 'extensions-dir': { type: 'string', deprecates: 'extensionHomePath', cat: 'e', args: 'dir', description: localize('extensionHomePath', "Set the root path for extensions.") }, - 'builtin-extensions-dir': { type: 'string' }, -+ 'extra-builtin-extensions-dir': { type: 'string[]', cat: 'o', description: 'Path to an extra builtin extension directory.' }, -+ 'extra-extensions-dir': { type: 'string[]', cat: 'o', description: 'Path to an extra user extension directory.' }, - 'list-extensions': { type: 'boolean', cat: 'e', description: localize('listExtensions', "List the installed extensions.") }, - 'show-versions': { type: 'boolean', cat: 'e', description: localize('showVersions', "Show versions of installed extensions, when using --list-extension.") }, - 'category': { type: 'string', cat: 'e', description: localize('category', "Filters installed extensions by provided category, when using --list-extension.") }, -@@ -310,4 +312,3 @@ export function buildHelpMessage(productName: string, executableName: string, ve - export function buildVersionMessage(version: string | undefined, commit: string | undefined): string { - return `${version || localize('unknownVersion', "Unknown version")}\n${commit || localize('unknownCommit', "Unknown commit")}\n${process.arch}`; - } -- -diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts -index 0428e1e888..9b3cddcb3a 100644 ---- a/src/vs/platform/environment/node/environmentService.ts -+++ b/src/vs/platform/environment/node/environmentService.ts -@@ -197,6 +197,13 @@ export class EnvironmentService implements IEnvironmentService { - return path.join(this.userHome, product.dataFolderName, 'extensions'); - } - -+ @memoize get extraExtensionPaths(): string[] { -+ return (this._args['extra-extensions-dir'] || []).map((p) => parsePathArg(p, process)); -+ } -+ @memoize get extraBuiltinExtensionPaths(): string[] { -+ return (this._args['extra-builtin-extensions-dir'] || []).map((p) => parsePathArg(p, process)); -+ } -+ - @memoize - get extensionDevelopmentLocationURI(): URI[] | undefined { - const s = this._args.extensionDevelopmentPath; -diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts -index 5b05650591..dc5140410e 100644 ---- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts -+++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts -@@ -743,11 +743,15 @@ export class ExtensionManagementService extends Disposable implements IExtension - - private scanSystemExtensions(): Promise { - this.logService.trace('Started scanning system extensions'); -- const systemExtensionsPromise = this.scanExtensions(this.systemExtensionsPath, ExtensionType.System) -- .then(result => { -- this.logService.trace('Scanned system extensions:', result.length); -- return result; -- }); -+ const systemExtensionsPromise = Promise.all([ -+ this.scanExtensions(this.systemExtensionsPath, ExtensionType.System), -+ ...this.environmentService.extraBuiltinExtensionPaths -+ .map((path) => this.scanExtensions(path, ExtensionType.System)) -+ ]).then((results) => { -+ const result = results.reduce((flat, current) => flat.concat(current), []); -+ this.logService.trace('Scanned system extensions:', result.length); -+ return result; -+ }); - if (this.environmentService.isBuilt) { - return systemExtensionsPromise; - } -@@ -769,9 +773,17 @@ export class ExtensionManagementService extends Disposable implements IExtension - .then(([systemExtensions, devSystemExtensions]) => [...systemExtensions, ...devSystemExtensions]); - } - -+ private scanAllUserExtensions(folderName: string, type: ExtensionType): Promise { -+ return Promise.all([ -+ this.scanExtensions(folderName, type), -+ ...this.environmentService.extraExtensionPaths.map((p) => this.scanExtensions(p, ExtensionType.User)) -+ ]).then((results) => results.reduce((flat, current) => flat.concat(current), [])); -+ } -+ -+ - private scanUserExtensions(excludeOutdated: boolean): Promise { - this.logService.trace('Started scanning user extensions'); -- return Promise.all([this.getUninstalledExtensions(), this.scanExtensions(this.extensionsPath, ExtensionType.User)]) -+ return Promise.all([this.getUninstalledExtensions(), this.scanAllUserExtensions(this.extensionsPath, ExtensionType.User)]) - .then(([uninstalled, extensions]) => { - extensions = extensions.filter(e => !uninstalled[new ExtensionIdentifierWithVersion(e.identifier, e.manifest.version).key()]); - if (excludeOutdated) { -@@ -786,6 +798,12 @@ export class ExtensionManagementService extends Disposable implements IExtension - private scanExtensions(root: string, type: ExtensionType): Promise { - const limiter = new Limiter(10); - return pfs.readdir(root) -+ .catch((error) => { -+ if (error.code !== 'ENOENT') { -+ throw error; -+ } -+ return []; -+ }) - .then(extensionsFolders => Promise.all(extensionsFolders.map(extensionFolder => limiter.queue(() => this.scanExtension(extensionFolder, root, type))))) - .then(extensions => extensions.filter(e => e && e.identifier)); - } -@@ -824,7 +842,7 @@ export class ExtensionManagementService extends Disposable implements IExtension - - private async removeUninstalledExtensions(): Promise { - const uninstalled = await this.getUninstalledExtensions(); -- const extensions = await this.scanExtensions(this.extensionsPath, ExtensionType.User); // All user extensions -+ const extensions = await this.scanAllUserExtensions(this.extensionsPath, ExtensionType.User); // All user extensions - const installed: Set = new Set(); - for (const e of extensions) { - if (!uninstalled[new ExtensionIdentifierWithVersion(e.identifier, e.manifest.version).key()]) { -@@ -843,7 +861,7 @@ export class ExtensionManagementService extends Disposable implements IExtension - } - - private removeOutdatedExtensions(): Promise { -- return this.scanExtensions(this.extensionsPath, ExtensionType.User) // All user extensions -+ return this.scanAllUserExtensions(this.extensionsPath, ExtensionType.User) // All user extensions - .then(extensions => { - const toRemove: ILocalExtension[] = []; - -diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts -index 804d113856..4b651e5c77 100644 ---- a/src/vs/platform/product/common/product.ts -+++ b/src/vs/platform/product/common/product.ts -@@ -22,11 +22,19 @@ if (isWeb) { - if (Object.keys(product).length === 0) { - assign(product, { - version: '1.41.0-dev', -+ codeServerVersion: 'dev', - nameLong: 'Visual Studio Code Web Dev', - nameShort: 'VSCode Web Dev', - urlProtocol: 'code-oss' - }); - } -+ -+ // NOTE@coder: enable injecting settings from the server. -+ const el = document.getElementById('vscode-remote-product-configuration'); -+ const rawProductConfiguration = el && el.getAttribute('data-settings'); -+ if (rawProductConfiguration) { -+ assign(product, JSON.parse(rawProductConfiguration)); -+ } - } - - // Node: AMD loader -@@ -36,7 +44,7 @@ else if (typeof require !== 'undefined' && typeof require.__$__nodeRequire === ' - const rootPath = path.dirname(getPathFromAmdModule(require, '')); - - product = assign({}, require.__$__nodeRequire(path.join(rootPath, 'product.json')) as IProductConfiguration); -- const pkg = require.__$__nodeRequire(path.join(rootPath, 'package.json')) as { version: string; }; -+ const pkg = require.__$__nodeRequire(path.join(rootPath, 'package.json')) as { version: string; codeServerVersion: string; }; - - // Running out of sources - if (env['VSCODE_DEV']) { -@@ -48,7 +56,8 @@ else if (typeof require !== 'undefined' && typeof require.__$__nodeRequire === ' - } - - assign(product, { -- version: pkg.version -+ version: pkg.version, -+ codeServerVersion: pkg.codeServerVersion, - }); - } - -diff --git a/src/vs/platform/product/common/productService.ts b/src/vs/platform/product/common/productService.ts -index 120fd66644..52547bdb0e 100644 ---- a/src/vs/platform/product/common/productService.ts -+++ b/src/vs/platform/product/common/productService.ts -@@ -16,6 +16,7 @@ export interface IProductService extends Readonly { - - export interface IProductConfiguration { - readonly version: string; -+ readonly codeServerVersion: string; - readonly date?: string; - readonly quality?: string; - readonly commit?: string; -diff --git a/src/vs/platform/remote/browser/browserSocketFactory.ts b/src/vs/platform/remote/browser/browserSocketFactory.ts -index d0f6e6b18a..1966fd297d 100644 ---- a/src/vs/platform/remote/browser/browserSocketFactory.ts -+++ b/src/vs/platform/remote/browser/browserSocketFactory.ts -@@ -205,7 +205,8 @@ export class BrowserSocketFactory implements ISocketFactory { - } - - connect(host: string, port: number, query: string, callback: IConnectCallback): void { -- const socket = this._webSocketFactory.create(`ws://${host}:${port}/?${query}&skipWebSocketFrames=false`); -+ // NOTE@coder: Modified to work against the current path. -+ const socket = this._webSocketFactory.create(`${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${window.location.host}${window.location.pathname}?${query}&skipWebSocketFrames=false`); - const errorListener = socket.onError((err) => callback(err, undefined)); - socket.onOpen(() => { - errorListener.dispose(); -@@ -213,6 +214,3 @@ export class BrowserSocketFactory implements ISocketFactory { - }); - } - } -- -- -- -diff --git a/src/vs/platform/request/common/request.ts b/src/vs/platform/request/common/request.ts -index 81ec255e65..c94829fc6a 100644 ---- a/src/vs/platform/request/common/request.ts -+++ b/src/vs/platform/request/common/request.ts -@@ -16,7 +16,7 @@ export const IRequestService = createDecorator('requestService' - export interface IRequestService { - _serviceBrand: undefined; - -- request(options: IRequestOptions, token: CancellationToken): Promise; -+ request(options: IRequestOptions, token: CancellationToken, gzip?: boolean): Promise; - - resolveProxy(url: string): Promise; - } -diff --git a/src/vs/platform/request/node/requestService.ts b/src/vs/platform/request/node/requestService.ts -index ad44dcbc33..7a7b5261ff 100644 ---- a/src/vs/platform/request/node/requestService.ts -+++ b/src/vs/platform/request/node/requestService.ts -@@ -57,7 +57,7 @@ export class RequestService extends Disposable implements IRequestService { - this.authorization = config.http && config.http.proxyAuthorization; - } - -- async request(options: NodeRequestOptions, token: CancellationToken): Promise { -+ async request(options: NodeRequestOptions, token: CancellationToken, gzip?: boolean): Promise { - this.logService.trace('RequestService#request', options.url); - - const { proxyUrl, strictSSL } = this; -@@ -70,7 +70,7 @@ export class RequestService extends Disposable implements IRequestService { - options.headers = assign(options.headers || {}, { 'Proxy-Authorization': this.authorization }); - } - -- return this._request(options, token); -+ return this._request(options, token, gzip); - } - - private async getNodeRequest(options: IRequestOptions): Promise { -@@ -79,7 +79,7 @@ export class RequestService extends Disposable implements IRequestService { - return module.request; - } - -- private _request(options: NodeRequestOptions, token: CancellationToken): Promise { -+ private _request(options: NodeRequestOptions, token: CancellationToken, gzip?: boolean): Promise { - - return new Promise(async (c, e) => { - let req: http.ClientRequest; -@@ -114,7 +114,7 @@ export class RequestService extends Disposable implements IRequestService { - } else { - let stream: streams.ReadableStream = res; - -- if (res.headers['content-encoding'] === 'gzip') { -+ if (gzip || res.headers['content-encoding'] === 'gzip') { - stream = res.pipe(createGunzip()); - } - -diff --git a/src/vs/platform/update/electron-main/abstractUpdateService.ts b/src/vs/platform/update/electron-main/abstractUpdateService.ts -index d8bf464fed..748715da3b 100644 ---- a/src/vs/platform/update/electron-main/abstractUpdateService.ts -+++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts -@@ -6,7 +6,6 @@ - import { Event, Emitter } from 'vs/base/common/event'; - import { timeout } from 'vs/base/common/async'; - import { IConfigurationService, getMigratedSettingValue } from 'vs/platform/configuration/common/configuration'; --import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; - import product from 'vs/platform/product/common/product'; - import { IUpdateService, State, StateType, AvailableForDownload, UpdateType } from 'vs/platform/update/common/update'; - import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -@@ -44,7 +43,7 @@ export abstract class AbstractUpdateService implements IUpdateService { - } - - constructor( -- @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, -+ _: any, // NOTE@coder: This depends on Electron so we skip it. - @IConfigurationService protected configurationService: IConfigurationService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, - @IRequestService protected requestService: IRequestService, -@@ -156,15 +155,8 @@ export abstract class AbstractUpdateService implements IUpdateService { - - this.logService.trace('update#quitAndInstall(): before lifecycle quit()'); - -- this.lifecycleMainService.quit(true /* from update */).then(vetod => { -- this.logService.trace(`update#quitAndInstall(): after lifecycle quit() with veto: ${vetod}`); -- if (vetod) { -- return; -- } -- - this.logService.trace('update#quitAndInstall(): running raw#quitAndInstall()'); - this.doQuitAndInstall(); -- }); - - return Promise.resolve(undefined); - } -diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts -index e69aa80159..2960d00456 100644 ---- a/src/vs/workbench/api/browser/extensionHost.contribution.ts -+++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts -@@ -62,6 +62,7 @@ import './mainThreadTunnelService'; - import './mainThreadAuthentication'; - import './mainThreadTimeline'; - import 'vs/workbench/api/common/apiCommands'; -+import 'vs/server/src/browser/mainThreadNodeProxy'; - - export class ExtensionPoints implements IWorkbenchContribution { - -diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts -index 91045fcda6..d93d3286d8 100644 ---- a/src/vs/workbench/api/common/extHost.api.impl.ts -+++ b/src/vs/workbench/api/common/extHost.api.impl.ts -@@ -72,6 +72,7 @@ import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelServ - import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; - import { ExtHostAuthentication } from 'vs/workbench/api/common/extHostAuthentication'; - import { ExtHostTimeline } from 'vs/workbench/api/common/extHostTimeline'; -+import { IExtHostNodeProxy } from 'vs/server/src/browser/extHostNodeProxy'; - - export interface IExtensionApiFactory { - (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; -@@ -93,6 +94,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I - const extHostLogService = accessor.get(ILogService); - const extHostTunnelService = accessor.get(IExtHostTunnelService); - const extHostApiDeprecation = accessor.get(IExtHostApiDeprecationService); -+ const extHostNodeProxy = accessor.get(IExtHostNodeProxy); - - // register addressable instances - rpcProtocol.set(ExtHostContext.ExtHostLogService, extHostLogService); -@@ -101,6 +103,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I - rpcProtocol.set(ExtHostContext.ExtHostExtensionService, extensionService); - rpcProtocol.set(ExtHostContext.ExtHostStorage, extHostStorage); - rpcProtocol.set(ExtHostContext.ExtHostTunnelService, extHostTunnelService); -+ rpcProtocol.set(ExtHostContext.ExtHostNodeProxy, extHostNodeProxy); - - // automatically create and register addressable instances - const extHostDecorations = rpcProtocol.set(ExtHostContext.ExtHostDecorations, accessor.get(IExtHostDecorations)); -diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts -index 55130ff918..032534b23e 100644 ---- a/src/vs/workbench/api/common/extHost.protocol.ts -+++ b/src/vs/workbench/api/common/extHost.protocol.ts -@@ -667,6 +667,16 @@ export interface MainThreadLabelServiceShape extends IDisposable { - $unregisterResourceLabelFormatter(handle: number): void; - } - -+export interface MainThreadNodeProxyShape extends IDisposable { -+ $send(message: string): void; -+} -+export interface ExtHostNodeProxyShape { -+ $onMessage(message: string): void; -+ $onClose(): void; -+ $onDown(): void; -+ $onUp(): void; -+} -+ - export interface MainThreadSearchShape extends IDisposable { - $registerFileSearchProvider(handle: number, scheme: string): void; - $registerTextSearchProvider(handle: number, scheme: string): void; -@@ -1498,7 +1508,8 @@ export const MainContext = { - MainThreadLabelService: createMainId('MainThreadLabelService'), - MainThreadTheming: createMainId('MainThreadTheming'), - MainThreadTunnelService: createMainId('MainThreadTunnelService'), -- MainThreadTimeline: createMainId('MainThreadTimeline') -+ MainThreadTimeline: createMainId('MainThreadTimeline'), -+ MainThreadNodeProxy: createMainId('MainThreadNodeProxy'), - }; - - export const ExtHostContext = { -@@ -1536,5 +1547,6 @@ export const ExtHostContext = { - ExtHostTheming: createMainId('ExtHostTheming'), - ExtHostTunnelService: createMainId('ExtHostTunnelService'), - ExtHostAuthentication: createMainId('ExtHostAuthentication'), -- ExtHostTimeline: createMainId('ExtHostTimeline') -+ ExtHostTimeline: createMainId('ExtHostTimeline'), -+ ExtHostNodeProxy: createMainId('ExtHostNodeProxy') - }; -diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts -index 978bf32fcd..a63954cce0 100644 ---- a/src/vs/workbench/api/common/extHostExtensionService.ts -+++ b/src/vs/workbench/api/common/extHostExtensionService.ts -@@ -5,7 +5,7 @@ - - import * as nls from 'vs/nls'; - import * as path from 'vs/base/common/path'; --import { originalFSPath, joinPath } from 'vs/base/common/resources'; -+import { originalFSPath } from 'vs/base/common/resources'; - import { Barrier } from 'vs/base/common/async'; - import { dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; - import { TernarySearchTree } from 'vs/base/common/map'; -@@ -33,6 +33,7 @@ import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePa - import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; - import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; - import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService'; -+import { IExtHostNodeProxy } from 'vs/server/src/browser/extHostNodeProxy'; - - interface ITestRunner { - /** Old test runner API, as exported from `vscode/lib/testrunner` */ -@@ -78,6 +79,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio - protected readonly _extHostConfiguration: ExtHostConfiguration; - protected readonly _logService: ILogService; - protected readonly _extHostTunnelService: IExtHostTunnelService; -+ protected readonly _nodeProxy: IExtHostNodeProxy; - - protected readonly _mainThreadWorkspaceProxy: MainThreadWorkspaceShape; - protected readonly _mainThreadTelemetryProxy: MainThreadTelemetryShape; -@@ -107,7 +109,8 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio - @ILogService logService: ILogService, - @IExtHostInitDataService initData: IExtHostInitDataService, - @IExtensionStoragePaths storagePath: IExtensionStoragePaths, -- @IExtHostTunnelService extHostTunnelService: IExtHostTunnelService -+ @IExtHostTunnelService extHostTunnelService: IExtHostTunnelService, -+ @IExtHostNodeProxy nodeProxy: IExtHostNodeProxy, - ) { - this._hostUtils = hostUtils; - this._extHostContext = extHostContext; -@@ -116,6 +119,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio - this._extHostWorkspace = extHostWorkspace; - this._extHostConfiguration = extHostConfiguration; - this._logService = logService; -+ this._nodeProxy = nodeProxy; - this._extHostTunnelService = extHostTunnelService; - this._disposables = new DisposableStore(); - -@@ -341,14 +345,14 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio - - const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup); - return Promise.all([ -- this._loadCommonJSModule(joinPath(extensionDescription.extensionLocation, extensionDescription.main), activationTimesBuilder), -+ this._loadCommonJSModule(extensionDescription, activationTimesBuilder), - this._loadExtensionContext(extensionDescription) - ]).then(values => { - return AbstractExtHostExtensionService._callActivate(this._logService, extensionDescription.identifier, values[0], values[1], activationTimesBuilder); - }); - } - -- protected abstract _loadCommonJSModule(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise; -+ protected abstract _loadCommonJSModule(module: URI | IExtensionDescription, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise; - - private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise { - -diff --git a/src/vs/workbench/api/node/extHost.services.ts b/src/vs/workbench/api/node/extHost.services.ts -index 72ad75d63e..8c4edee5e3 100644 ---- a/src/vs/workbench/api/node/extHost.services.ts -+++ b/src/vs/workbench/api/node/extHost.services.ts -@@ -29,6 +29,8 @@ import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService'; - import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService'; - import { ExtHostTunnelService } from 'vs/workbench/api/node/extHostTunnelService'; - import { IExtHostApiDeprecationService, ExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; -+import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; -+import { IExtHostNodeProxy } from 'vs/server/src/browser/extHostNodeProxy'; - - // register singleton services - registerSingleton(ILogService, ExtHostLogService); -@@ -47,3 +49,19 @@ registerSingleton(IExtensionStoragePaths, ExtensionStoragePaths); - registerSingleton(IExtHostExtensionService, ExtHostExtensionService); - registerSingleton(IExtHostStorage, ExtHostStorage); - registerSingleton(IExtHostTunnelService, ExtHostTunnelService); -+ -+function NotImplementedProxy(name: ServiceIdentifier): { new(): T } { -+ return class { -+ constructor() { -+ return new Proxy({}, { -+ get(target: any, prop: string | number) { -+ if (target[prop]) { -+ return target[prop]; -+ } -+ throw new Error(`Not Implemented: ${name}->${String(prop)}`); -+ } -+ }); -+ } -+ }; -+} -+registerSingleton(IExtHostNodeProxy, class extends NotImplementedProxy(IExtHostNodeProxy) {}); -diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts -index a1c3e50ffd..910627aaf9 100644 ---- a/src/vs/workbench/api/node/extHostExtensionService.ts -+++ b/src/vs/workbench/api/node/extHostExtensionService.ts -@@ -13,6 +13,8 @@ import { ExtHostDownloadService } from 'vs/workbench/api/node/extHostDownloadSer - import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer'; - import { URI } from 'vs/base/common/uri'; - import { Schemas } from 'vs/base/common/network'; -+import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -+import { joinPath } from 'vs/base/common/resources'; - - class NodeModuleRequireInterceptor extends RequireInterceptor { - -@@ -76,7 +78,10 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { - }; - } - -- protected _loadCommonJSModule(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { -+ protected _loadCommonJSModule(module: URI | IExtensionDescription, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { -+ if (!URI.isUri(module)) { -+ module = joinPath(module.extensionLocation, module.main!); -+ } - if (module.scheme !== Schemas.file) { - throw new Error(`Cannot load URI: '${module}', must be of file-scheme`); - } -diff --git a/src/vs/workbench/api/node/extHostStoragePaths.ts b/src/vs/workbench/api/node/extHostStoragePaths.ts -index afdd6bf398..ac91318ce3 100644 ---- a/src/vs/workbench/api/node/extHostStoragePaths.ts -+++ b/src/vs/workbench/api/node/extHostStoragePaths.ts -@@ -5,13 +5,14 @@ - - import * as path from 'vs/base/common/path'; - import { URI } from 'vs/base/common/uri'; --import * as pfs from 'vs/base/node/pfs'; --import { IEnvironment, IStaticWorkspaceData } from 'vs/workbench/api/common/extHost.protocol'; -+import { IEnvironment, IStaticWorkspaceData, MainContext } from 'vs/workbench/api/common/extHost.protocol'; - import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; - import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; - import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; - import { withNullAsUndefined } from 'vs/base/common/types'; - import { ILogService } from 'vs/platform/log/common/log'; -+import { IExtHostRpcService } from '../common/extHostRpcService'; -+import { VSBuffer } from 'vs/base/common/buffer'; - - export class ExtensionStoragePaths implements IExtensionStoragePaths { - -@@ -26,6 +27,7 @@ export class ExtensionStoragePaths implements IExtensionStoragePaths { - constructor( - @IExtHostInitDataService initData: IExtHostInitDataService, - @ILogService private readonly _logService: ILogService, -+ @IExtHostRpcService private readonly _extHostRpc: IExtHostRpcService, - ) { - this._workspace = withNullAsUndefined(initData.workspace); - this._environment = initData.environment; -@@ -54,21 +56,25 @@ export class ExtensionStoragePaths implements IExtensionStoragePaths { - const storageName = this._workspace.id; - const storagePath = path.join(this._environment.appSettingsHome.fsPath, 'workspaceStorage', storageName); - -- const exists = await pfs.dirExists(storagePath); -+ // NOTE@coder: Use the file system proxy so this will work in the browser. -+ // writeFile performs a mkdirp so we don't need to bother ourselves. -+ const fileSystem = this._extHostRpc.getProxy(MainContext.MainThreadFileSystem); -+ const exists = fileSystem.$stat(URI.file(storagePath)) - - if (exists) { - return storagePath; - } - - try { -- await pfs.mkdirp(storagePath); -- await pfs.writeFile( -- path.join(storagePath, 'meta.json'), -- JSON.stringify({ -- id: this._workspace.id, -- configuration: this._workspace.configuration && URI.revive(this._workspace.configuration).toString(), -- name: this._workspace.name -- }, undefined, 2) -+ await fileSystem.$writeFile( -+ URI.file(path.join(storagePath, 'meta.json')), -+ VSBuffer.fromString( -+ JSON.stringify({ -+ id: this._workspace.id, -+ configuration: this._workspace.configuration && URI.revive(this._workspace.configuration).toString(), -+ name: this._workspace.name -+ }, undefined, 2) -+ ) - ); - return storagePath; - -diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts -index 4781f22676..25143a97c0 100644 ---- a/src/vs/workbench/api/worker/extHostExtensionService.ts -+++ b/src/vs/workbench/api/worker/extHostExtensionService.ts -@@ -9,6 +9,9 @@ import { AbstractExtHostExtensionService } from 'vs/workbench/api/common/extHost - import { endsWith } from 'vs/base/common/strings'; - import { URI } from 'vs/base/common/uri'; - import { RequireInterceptor } from 'vs/workbench/api/common/extHostRequireInterceptor'; -+import { joinPath } from 'vs/base/common/resources'; -+import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -+import { loadCommonJSModule } from 'vs/server/src/browser/worker'; - - class WorkerRequireInterceptor extends RequireInterceptor { - -@@ -41,7 +44,14 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { - await this._fakeModules.install(); - } - -- protected async _loadCommonJSModule(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { -+ protected async _loadCommonJSModule(module: URI | IExtensionDescription, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { -+ if (!URI.isUri(module) && module.extensionKind !== 'web') { -+ return loadCommonJSModule(module, activationTimesBuilder, this._nodeProxy, this._logService, this._fakeModules!.getModule('vscode', module.extensionLocation)); -+ } -+ -+ if (!URI.isUri(module)) { -+ module = joinPath(module.extensionLocation, module.main!); -+ } - - module = module.with({ path: ensureSuffix(module.path, '.js') }); - const response = await fetch(module.toString(true)); -@@ -57,7 +67,7 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { - const _exports = {}; - const _module = { exports: _exports }; - const _require = (request: string) => { -- const result = this._fakeModules!.getModule(request, module); -+ const result = this._fakeModules!.getModule(request, module); - if (result === undefined) { - throw new Error(`Cannot load module '${request}'`); - } -diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts -index 94e7052574..7e5563b417 100644 ---- a/src/vs/workbench/browser/web.main.ts -+++ b/src/vs/workbench/browser/web.main.ts -@@ -49,6 +49,7 @@ import { IndexedDBLogProvider } from 'vs/workbench/services/log/browser/indexedD - import { InMemoryLogProvider } from 'vs/workbench/services/log/common/inMemoryLogProvider'; - import { isWorkspaceToOpen, isFolderToOpen } from 'vs/platform/windows/common/windows'; - import { getWorkspaceIdentifier } from 'vs/workbench/services/workspaces/browser/workspaces'; -+import { initialize } from 'vs/server/src/browser/client'; - - class BrowserMain extends Disposable { - -@@ -85,6 +86,7 @@ class BrowserMain extends Disposable { - - // Startup - workbench.startup(); -+ await initialize(services.serviceCollection); - } - - private registerListeners(workbench: Workbench, storageService: BrowserStorageService): void { -diff --git a/src/vs/workbench/common/resources.ts b/src/vs/workbench/common/resources.ts -index c509716fc4..e416413084 100644 ---- a/src/vs/workbench/common/resources.ts -+++ b/src/vs/workbench/common/resources.ts -@@ -15,6 +15,7 @@ import { ParsedExpression, IExpression, parse } from 'vs/base/common/glob'; - import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; - import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; - import { withNullAsUndefined } from 'vs/base/common/types'; -+import { Schemas } from 'vs/base/common/network'; - - export class ResourceContextKey extends Disposable implements IContextKey { - -@@ -63,7 +64,8 @@ export class ResourceContextKey extends Disposable implements IContextKey { - set(value: URI | null) { - if (!ResourceContextKey._uriEquals(this._resourceKey.get(), value)) { - this._resourceKey.set(value); -- this._schemeKey.set(value ? value.scheme : null); -+ // NOTE@coder: fixes extensions matching against file schemas. -+ this._schemeKey.set(value ? (value.scheme === Schemas.vscodeRemote ? Schemas.file : value.scheme) : null); - this._filenameKey.set(value ? basename(value) : null); - this._langIdKey.set(value ? this._modeService.getModeIdByFilepathOrFirstLine(value) : null); - this._extensionKey.set(value ? extname(value) : null); -diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js -index 63c9af47e2..021358fef9 100644 ---- a/src/vs/workbench/contrib/webview/browser/pre/main.js -+++ b/src/vs/workbench/contrib/webview/browser/pre/main.js -@@ -329,7 +329,8 @@ - if (data.endpoint) { - try { - const endpointUrl = new URL(data.endpoint); -- csp.setAttribute('content', csp.getAttribute('content').replace(/vscode-resource:(?=(\s|;|$))/g, endpointUrl.origin)); -+ // NOTE@coder: Add back the trailing slash so it'll work for sub-paths. -+ csp.setAttribute('content', csp.getAttribute('content').replace(/vscode-resource:(?=(\s|;|$))/g, endpointUrl.origin + "/")); - } catch (e) { - console.error('Could not rewrite csp'); - } -diff --git a/src/vs/workbench/services/dialogs/browser/dialogService.ts b/src/vs/workbench/services/dialogs/browser/dialogService.ts -index f67f9aa064..add754cd5a 100644 ---- a/src/vs/workbench/services/dialogs/browser/dialogService.ts -+++ b/src/vs/workbench/services/dialogs/browser/dialogService.ts -@@ -122,11 +122,12 @@ export class DialogService implements IDialogService { - - async about(): Promise { - const detail = nls.localize('aboutDetail', -- "Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}", -+ "Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}\nCode Server Version: {4}", - this.productService.version || 'Unknown', - this.productService.commit || 'Unknown', - this.productService.date || 'Unknown', -- navigator.userAgent -+ navigator.userAgent, -+ this.productService.codeServerVersion || 'Unknown', - ); - - const { choice } = await this.show(Severity.Info, this.productService.nameLong, [nls.localize('copy', "Copy"), nls.localize('ok', "OK")], { detail, cancelId: 1 }); -diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts -index 1bf4cfad2a..924a2fcd87 100644 ---- a/src/vs/workbench/services/environment/browser/environmentService.ts -+++ b/src/vs/workbench/services/environment/browser/environmentService.ts -@@ -195,8 +195,8 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment - - @memoize - get webviewExternalEndpoint(): string { -- // TODO: get fallback from product.json -- return (this.options.webviewEndpoint || 'https://{{uuid}}.vscode-webview-test.com/{{commit}}').replace('{{commit}}', product.commit || '0d728c31ebdf03869d2687d9be0b017667c9ff37'); -+ // NOTE@coder: Modified to work against the current URL. -+ return `${window.location.origin}${window.location.pathname.replace(/\/+$/, '')}/webview/`; - } - - @memoize -@@ -249,6 +249,8 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment - installSourcePath!: string; - - builtinExtensionsPath!: string; -+ extraExtensionPaths!: string[]; -+ extraBuiltinExtensionPaths!: string[]; - - globalStorageHome!: string; - workspaceStorageHome!: string; -diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts -index fe891a042e..21d0d4bf61 100644 ---- a/src/vs/workbench/services/extensions/browser/extensionService.ts -+++ b/src/vs/workbench/services/extensions/browser/extensionService.ts -@@ -119,6 +119,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten - - } else { - // remote: only enabled and none-web'ish extension -+ localExtensions.push(...remoteEnv.extensions.filter(extension => this._isEnabled(extension) && canExecuteOnWeb(extension, this._productService, this._configService))); - remoteEnv.extensions = remoteEnv.extensions.filter(extension => this._isEnabled(extension) && !canExecuteOnWeb(extension, this._productService, this._configService)); - this._checkEnableProposedApi(remoteEnv.extensions); - -diff --git a/src/vs/workbench/services/extensions/common/extensionsUtil.ts b/src/vs/workbench/services/extensions/common/extensionsUtil.ts -index 9e8352ac88..2d1cb0a107 100644 ---- a/src/vs/workbench/services/extensions/common/extensionsUtil.ts -+++ b/src/vs/workbench/services/extensions/common/extensionsUtil.ts -@@ -32,7 +32,8 @@ export function canExecuteOnWorkspace(manifest: IExtensionManifest, productServi - - export function canExecuteOnWeb(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean { - const extensionKind = getExtensionKind(manifest, productService, configurationService); -- return extensionKind.some(kind => kind === 'web'); -+ // NOTE@coder: hardcode vim for now. -+ return extensionKind.some(kind => kind === 'web') || manifest.name === 'vim'; - } - - export function getExtensionKind(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): ExtensionKind[] { -diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts -index 0f35c54431..32fff09b18 100644 ---- a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts -+++ b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts -@@ -53,12 +53,13 @@ const args = minimist(process.argv.slice(2), { - const Module = require.__$__nodeRequire('module') as any; - const originalLoad = Module._load; - -- Module._load = function (request: string) { -+ Module._load = function (request: string, parent: object, isMain: boolean) { - if (request === 'natives') { - throw new Error('Either the extension or a NPM dependency is using the "natives" node module which is unsupported as it can cause a crash of the extension host. Click [here](https://go.microsoft.com/fwlink/?linkid=871887) to find out more'); - } - -- return originalLoad.apply(this, arguments); -+ // NOTE@coder: Map node_module.asar requests to regular node_modules. -+ return originalLoad.apply(this, [request.replace(/node_modules\.asar(\.unpacked)?/, 'node_modules'), parent, isMain]); - }; - })(); - -@@ -131,8 +132,11 @@ function _createExtHostProtocol(): Promise { - - // Wait for rich client to reconnect - protocol.onSocketClose(() => { -- // The socket has closed, let's give the renderer a certain amount of time to reconnect -- disconnectRunner1.schedule(); -+ // NOTE@coder: Inform the server so we can manage offline -+ // connections there instead. Our goal is to persist connections -+ // forever (to a reasonable point) to account for things like -+ // hibernating overnight. -+ process.send!({ type: 'VSCODE_EXTHOST_DISCONNECTED' }); - }); - } - } -diff --git a/src/vs/workbench/services/extensions/worker/extHost.services.ts b/src/vs/workbench/services/extensions/worker/extHost.services.ts -index bbb72e9511..63f1f6ff46 100644 ---- a/src/vs/workbench/services/extensions/worker/extHost.services.ts -+++ b/src/vs/workbench/services/extensions/worker/extHost.services.ts -@@ -18,11 +18,12 @@ import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePa - import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; - import { IExtHostStorage, ExtHostStorage } from 'vs/workbench/api/common/extHostStorage'; - import { ExtHostExtensionService } from 'vs/workbench/api/worker/extHostExtensionService'; --import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; - import { ILogService } from 'vs/platform/log/common/log'; - import { ExtHostLogService } from 'vs/workbench/api/worker/extHostLogService'; - import { IExtHostTunnelService, ExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService'; - import { IExtHostApiDeprecationService, ExtHostApiDeprecationService, } from 'vs/workbench/api/common/extHostApiDeprecationService'; -+import { ExtHostNodeProxy, IExtHostNodeProxy } from 'vs/server/src/browser/extHostNodeProxy'; -+import { ExtensionStoragePaths } from 'vs/workbench/api/node/extHostStoragePaths'; - - // register singleton services - registerSingleton(ILogService, ExtHostLogService); -@@ -37,23 +38,9 @@ registerSingleton(IExtHostStorage, ExtHostStorage); - registerSingleton(IExtHostExtensionService, ExtHostExtensionService); - registerSingleton(IExtHostSearch, ExtHostSearch); - registerSingleton(IExtHostTunnelService, ExtHostTunnelService); -+registerSingleton(IExtHostNodeProxy, ExtHostNodeProxy); - --// register services that only throw errors --function NotImplementedProxy(name: ServiceIdentifier): { new(): T } { -- return class { -- constructor() { -- return new Proxy({}, { -- get(target: any, prop: string | number) { -- if (target[prop]) { -- return target[prop]; -- } -- throw new Error(`Not Implemented: ${name}->${String(prop)}`); -- } -- }); -- } -- }; --} - registerSingleton(IExtHostTerminalService, WorkerExtHostTerminalService); - registerSingleton(IExtHostTask, WorkerExtHostTask); - registerSingleton(IExtHostDebugService, WorkerExtHostDebugService); --registerSingleton(IExtensionStoragePaths, class extends NotImplementedProxy(IExtensionStoragePaths) { whenReady = Promise.resolve(); }); -+registerSingleton(IExtensionStoragePaths, ExtensionStoragePaths); -diff --git a/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts b/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts -index 99394090da..4891e0fece 100644 ---- a/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts -+++ b/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts -@@ -5,17 +5,17 @@ - - import { createChannelSender } from 'vs/base/parts/ipc/node/ipc'; - import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; --import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; - import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -+import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; - - export class LocalizationsService { - - _serviceBrand: undefined; - - constructor( -- @ISharedProcessService sharedProcessService: ISharedProcessService, -+ @IRemoteAgentService remoteAgentService: IRemoteAgentService, - ) { -- return createChannelSender(sharedProcessService.getChannel('localizations')); -+ return createChannelSender(remoteAgentService.getConnection()!.getChannel('localizations')); - } - } - -diff --git a/src/vs/workbench/services/update/electron-browser/updateService.ts b/src/vs/workbench/services/update/electron-browser/updateService.ts -index b8f6558b2c..7aeafe6e0e 100644 ---- a/src/vs/workbench/services/update/electron-browser/updateService.ts -+++ b/src/vs/workbench/services/update/electron-browser/updateService.ts -@@ -6,8 +6,8 @@ - import { IChannel } from 'vs/base/parts/ipc/common/ipc'; - import { Event, Emitter } from 'vs/base/common/event'; - import { IUpdateService, State } from 'vs/platform/update/common/update'; --import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; - import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -+import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; - - export class NativeUpdateService implements IUpdateService { - -@@ -21,8 +21,9 @@ export class NativeUpdateService implements IUpdateService { - - private channel: IChannel; - -- constructor(@IMainProcessService mainProcessService: IMainProcessService) { -- this.channel = mainProcessService.getChannel('update'); -+ // NOTE@coder: patched to work in the browser. -+ constructor(@IRemoteAgentService remoteAgentService: IRemoteAgentService) { -+ this.channel = remoteAgentService.getConnection()!.getChannel('update'); - - // always set this._state as the state changes - this.onStateChange(state => this._state = state); -diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts -index 0719b361e0..3a4c5cefe8 100644 ---- a/src/vs/workbench/workbench.web.main.ts -+++ b/src/vs/workbench/workbench.web.main.ts -@@ -34,11 +34,14 @@ import 'vs/workbench/services/textfile/browser/browserTextFileService'; - import 'vs/workbench/services/keybinding/browser/keymapService'; - import 'vs/workbench/services/extensions/browser/extensionService'; - import 'vs/workbench/services/extensionManagement/common/extensionManagementServerService'; --import 'vs/workbench/services/telemetry/browser/telemetryService'; -+// NOTE@coder: We send it all to the server side to be processed there instead. -+// import 'vs/workbench/services/telemetry/browser/telemetryService'; - import 'vs/workbench/services/configurationResolver/browser/configurationResolverService'; - import 'vs/workbench/services/credentials/browser/credentialsService'; - import 'vs/workbench/services/url/browser/urlService'; --import 'vs/workbench/services/update/browser/updateService'; -+// NOTE@coder: Use the electron-browser version since it already comes with a -+// channel which lets us actually perform updates. -+import 'vs/workbench/services/update/electron-browser/updateService'; - import 'vs/workbench/contrib/tags/browser/workspaceTagsService'; - import 'vs/workbench/services/workspaces/browser/workspacesService'; - import 'vs/workbench/services/workspaces/browser/workspaceEditingService'; diff --git a/src/browser/client.ts b/src/browser/client.ts deleted file mode 100644 index 544bd6f7a..000000000 --- a/src/browser/client.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { Emitter } from "vs/base/common/event"; -import { URI } from "vs/base/common/uri"; -import { localize } from "vs/nls"; -import { Extensions, IConfigurationRegistry } from "vs/platform/configuration/common/configurationRegistry"; -import { registerSingleton } from "vs/platform/instantiation/common/extensions"; -import { ServiceCollection } from "vs/platform/instantiation/common/serviceCollection"; -import { ILocalizationsService } from "vs/platform/localizations/common/localizations"; -import { INotificationService, Severity } from "vs/platform/notification/common/notification"; -import { Registry } from "vs/platform/registry/common/platform"; -import { PersistentConnectionEventType } from "vs/platform/remote/common/remoteAgentConnection"; -import { ITelemetryService } from "vs/platform/telemetry/common/telemetry"; -import { INodeProxyService, NodeProxyChannelClient } from "vs/server/src/common/nodeProxy"; -import { TelemetryChannelClient } from "vs/server/src/common/telemetry"; -import { split } from "vs/server/src/common/util"; -import "vs/workbench/contrib/localizations/browser/localizations.contribution"; -import { LocalizationsService } from "vs/workbench/services/localizations/electron-browser/localizationsService"; -import { IRemoteAgentService } from "vs/workbench/services/remote/common/remoteAgentService"; - -class TelemetryService extends TelemetryChannelClient { - public constructor( - @IRemoteAgentService remoteAgentService: IRemoteAgentService, - ) { - super(remoteAgentService.getConnection()!.getChannel("telemetry")); - } -} - -const TELEMETRY_SECTION_ID = "telemetry"; - -Registry.as(Extensions.Configuration).registerConfiguration({ - "id": TELEMETRY_SECTION_ID, - "order": 110, - "type": "object", - "title": localize("telemetryConfigurationTitle", "Telemetry"), - "properties": { - "telemetry.enableTelemetry": { - "type": "boolean", - "description": localize("telemetry.enableTelemetry", "Enable usage data and errors to be sent to a Microsoft online service."), - "default": true, - "tags": ["usesOnlineServices"] - } - } -}); - -class NodeProxyService extends NodeProxyChannelClient implements INodeProxyService { - private readonly _onClose = new Emitter(); - public readonly onClose = this._onClose.event; - private readonly _onDown = new Emitter(); - public readonly onDown = this._onDown.event; - private readonly _onUp = new Emitter(); - public readonly onUp = this._onUp.event; - - public constructor( - @IRemoteAgentService remoteAgentService: IRemoteAgentService, - ) { - super(remoteAgentService.getConnection()!.getChannel("nodeProxy")); - remoteAgentService.getConnection()!.onDidStateChange((state) => { - switch (state.type) { - case PersistentConnectionEventType.ConnectionGain: - return this._onUp.fire(); - case PersistentConnectionEventType.ConnectionLost: - return this._onDown.fire(); - case PersistentConnectionEventType.ReconnectionPermanentFailure: - return this._onClose.fire(); - } - }); - } -} - -registerSingleton(ILocalizationsService, LocalizationsService); -registerSingleton(INodeProxyService, NodeProxyService); -registerSingleton(ITelemetryService, TelemetryService); - -/** - * This is called by vs/workbench/browser/web.main.ts after the workbench has - * been initialized so we can initialize our own client-side code. - */ -export const initialize = async (services: ServiceCollection): Promise => { - const event = new CustomEvent("ide-ready"); - window.dispatchEvent(event); - - if (!window.isSecureContext) { - (services.get(INotificationService) as INotificationService).notify({ - severity: Severity.Warning, - message: "code-server is being accessed over an insecure domain. Some functionality may not work as expected.", - actions: { - primary: [{ - id: "understand", - label: "I understand", - tooltip: "", - class: undefined, - enabled: true, - checked: true, - dispose: () => undefined, - run: () => { - return Promise.resolve(); - } - }], - } - }); - } -}; - -export interface Query { - [key: string]: string | undefined; -} - -/** - * Return the URL modified with the specified query variables. It's pretty - * stupid so it probably doesn't cover any edge cases. Undefined values will - * unset existing values. Doesn't allow duplicates. - */ -export const withQuery = (url: string, replace: Query): string => { - const uri = URI.parse(url); - const query = { ...replace }; - uri.query.split("&").forEach((kv) => { - const [key, value] = split(kv, "="); - if (!(key in query)) { - query[key] = value; - } - }); - return uri.with({ - query: Object.keys(query) - .filter((k) => typeof query[k] !== "undefined") - .map((k) => `${k}=${query[k]}`).join("&"), - }).toString(true); -}; diff --git a/src/browser/extHostNodeProxy.ts b/src/browser/extHostNodeProxy.ts deleted file mode 100644 index 63ded64ea..000000000 --- a/src/browser/extHostNodeProxy.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Emitter } from "vs/base/common/event"; -import { createDecorator } from "vs/platform/instantiation/common/instantiation"; -import { ExtHostNodeProxyShape, MainContext, MainThreadNodeProxyShape } from "vs/workbench/api/common/extHost.protocol"; -import { IExtHostRpcService } from "vs/workbench/api/common/extHostRpcService"; - -export class ExtHostNodeProxy implements ExtHostNodeProxyShape { - _serviceBrand: any; - - private readonly _onMessage = new Emitter(); - public readonly onMessage = this._onMessage.event; - private readonly _onClose = new Emitter(); - public readonly onClose = this._onClose.event; - private readonly _onDown = new Emitter(); - public readonly onDown = this._onDown.event; - private readonly _onUp = new Emitter(); - public readonly onUp = this._onUp.event; - - private readonly proxy: MainThreadNodeProxyShape; - - constructor(@IExtHostRpcService rpc: IExtHostRpcService) { - this.proxy = rpc.getProxy(MainContext.MainThreadNodeProxy); - } - - public $onMessage(message: string): void { - this._onMessage.fire(message); - } - - public $onClose(): void { - this._onClose.fire(); - } - - public $onUp(): void { - this._onUp.fire(); - } - - public $onDown(): void { - this._onDown.fire(); - } - - public send(message: string): void { - this.proxy.$send(message); - } -} - -export interface IExtHostNodeProxy extends ExtHostNodeProxy { } -export const IExtHostNodeProxy = createDecorator("IExtHostNodeProxy"); diff --git a/src/browser/login.html b/src/browser/login.html deleted file mode 100644 index 0440c4ede..000000000 --- a/src/browser/login.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - Authenticate: code-server - - - - - - - - - - diff --git a/src/browser/mainThreadNodeProxy.ts b/src/browser/mainThreadNodeProxy.ts deleted file mode 100644 index ab23d3ad9..000000000 --- a/src/browser/mainThreadNodeProxy.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { IDisposable } from "vs/base/common/lifecycle"; -import { INodeProxyService } from "vs/server/src/common/nodeProxy"; -import { ExtHostContext, IExtHostContext, MainContext, MainThreadNodeProxyShape } from "vs/workbench/api/common/extHost.protocol"; -import { extHostNamedCustomer } from "vs/workbench/api/common/extHostCustomers"; - -@extHostNamedCustomer(MainContext.MainThreadNodeProxy) -export class MainThreadNodeProxy implements MainThreadNodeProxyShape { - private disposed = false; - private disposables = []; - - constructor( - extHostContext: IExtHostContext, - @INodeProxyService private readonly proxyService: INodeProxyService, - ) { - if (!extHostContext.remoteAuthority) { // HACK: A terrible way to detect if running in the worker. - const proxy = extHostContext.getProxy(ExtHostContext.ExtHostNodeProxy); - this.disposables = [ - this.proxyService.onMessage((message: string) => proxy.$onMessage(message)), - this.proxyService.onClose(() => proxy.$onClose()), - this.proxyService.onDown(() => proxy.$onDown()), - this.proxyService.onUp(() => proxy.$onUp()), - ]; - } - } - - $send(message: string): void { - if (!this.disposed) { - this.proxyService.send(message); - } - } - - dispose(): void { - this.disposables.forEach((d) => d.dispose()); - this.disposables = []; - this.disposed = true; - } -} diff --git a/src/browser/media/favicon.ico b/src/browser/media/favicon.ico new file mode 100644 index 000000000..98c13ca5e Binary files /dev/null and b/src/browser/media/favicon.ico differ diff --git a/src/browser/media/manifest.json b/src/browser/media/manifest.json new file mode 100644 index 000000000..76ec15ceb --- /dev/null +++ b/src/browser/media/manifest.json @@ -0,0 +1,40 @@ +{ + "name": "code-server", + "short_name": "code-server", + "start_url": "{{BASE}}", + "display": "fullscreen", + "background-color": "#fff", + "description": "Run editors on a remote server.", + "icons": [ + { + "src": "{{BASE}}/static/{{COMMIT}}/src/browser/media/pwa-icon-96.png", + "type": "image/png", + "sizes": "96x96" + }, + { + "src": "{{BASE}}/static/{{COMMIT}}/src/browser/media/pwa-icon-128.png", + "type": "image/png", + "sizes": "128x128" + }, + { + "src": "{{BASE}}/static/{{COMMIT}}/src/browser/media/pwa-icon-192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "{{BASE}}/static/{{COMMIT}}/src/browser/media/pwa-icon-256.png", + "type": "image/png", + "sizes": "256x256" + }, + { + "src": "{{BASE}}/static/{{COMMIT}}/src/browser/media/pwa-icon-384.png", + "type": "image/png", + "sizes": "384x384" + }, + { + "src": "{{BASE}}/static/{{COMMIT}}/src/browser/media/pwa-icon-512.png", + "type": "image/png", + "sizes": "512x512" + } + ] +} diff --git a/src/browser/media/pwa-icon-128.png b/src/browser/media/pwa-icon-128.png new file mode 100644 index 000000000..784c750b7 Binary files /dev/null and b/src/browser/media/pwa-icon-128.png differ diff --git a/src/browser/media/pwa-icon-192.png b/src/browser/media/pwa-icon-192.png new file mode 100644 index 000000000..654db4b9f Binary files /dev/null and b/src/browser/media/pwa-icon-192.png differ diff --git a/src/browser/media/pwa-icon-256.png b/src/browser/media/pwa-icon-256.png new file mode 100644 index 000000000..122e55c7d Binary files /dev/null and b/src/browser/media/pwa-icon-256.png differ diff --git a/src/browser/media/pwa-icon-384.png b/src/browser/media/pwa-icon-384.png new file mode 100644 index 000000000..f7ce48b51 Binary files /dev/null and b/src/browser/media/pwa-icon-384.png differ diff --git a/src/browser/media/pwa-icon-512.png b/src/browser/media/pwa-icon-512.png new file mode 100644 index 000000000..82cbfd41c Binary files /dev/null and b/src/browser/media/pwa-icon-512.png differ diff --git a/src/browser/media/pwa-icon-96.png b/src/browser/media/pwa-icon-96.png new file mode 100644 index 000000000..9471052da Binary files /dev/null and b/src/browser/media/pwa-icon-96.png differ diff --git a/src/browser/pages/app.css b/src/browser/pages/app.css new file mode 100644 index 000000000..ae091a2eb --- /dev/null +++ b/src/browser/pages/app.css @@ -0,0 +1,24 @@ +/* NOTE: Disable scrollbars since an oversized element creates them. */ +.app-input { + height: 100%; + left: 0; + outline: none; + position: fixed; + scrollbar-width: none; + top: 0; + width: 100%; + z-index: 20; +} + +.app-input::-webkit-scrollbar { + display: none; +} + +.app-render { + height: 100%; + left: 0; + position: fixed; + top: 0; + width: 100%; + z-index: 10; +} diff --git a/src/browser/pages/app.html b/src/browser/pages/app.html new file mode 100644 index 000000000..bfd58737a --- /dev/null +++ b/src/browser/pages/app.html @@ -0,0 +1,25 @@ + + + + + + + code-server — {{APP_NAME}} + + + + + + + + + + + diff --git a/src/browser/pages/app.ts b/src/browser/pages/app.ts new file mode 100644 index 000000000..4922dc2de --- /dev/null +++ b/src/browser/pages/app.ts @@ -0,0 +1,12 @@ +import { getOptions } from "../../common/util" + +import "./app.css" +import "./error.css" +import "./global.css" +import "./home.css" +import "./login.css" +import "./update.css" + +const options = getOptions() + +console.log(options) diff --git a/src/browser/pages/error.css b/src/browser/pages/error.css new file mode 100644 index 000000000..15b7d4aca --- /dev/null +++ b/src/browser/pages/error.css @@ -0,0 +1,32 @@ +.error-display { + box-sizing: border-box; + padding: 20px; + text-align: center; +} + +.error-display > .header { + font-size: 6rem; + margin: 0; +} + +.error-display > .body { + color: #444; + font-size: 1.2rem; +} + +.error-display > .links { + margin-top: 16px; +} + +.error-display > .links > .link { + color: rgb(87, 114, 245); + text-decoration: none; +} + +.error-display > .links > .link:hover { + text-decoration: underline; +} + +.error-display .success { + color: green; +} diff --git a/src/browser/pages/error.html b/src/browser/pages/error.html new file mode 100644 index 000000000..0e3892215 --- /dev/null +++ b/src/browser/pages/error.html @@ -0,0 +1,35 @@ + + + + + + + {{ERROR_TITLE}} - code-server + + + + + + + +
+
+

{{ERROR_HEADER}}

+
+ {{ERROR_BODY}} +
+ +
+
+ + + diff --git a/src/browser/pages/global.css b/src/browser/pages/global.css new file mode 100644 index 000000000..9a22645bd --- /dev/null +++ b/src/browser/pages/global.css @@ -0,0 +1,76 @@ +html, +body, +#root { + height: 100%; + width: 100%; +} + +body { + background: rgb(244, 247, 252); + color: #111; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", + "Segoe UI Emoji", "Segoe UI Symbol"; + overflow: hidden; +} + +input, +button { + font-family: inherit; + font-size: 1rem; + line-height: 1rem; +} + +.-button { + background-color: rgb(87, 114, 245); + border-radius: 5px; + border: none; + box-sizing: border-box; + color: white; + cursor: pointer; + padding: 18px 20px; + text-decoration: none; +} + +.center-container { + align-items: center; + box-sizing: border-box; + display: flex; + flex-direction: column; + justify-content: center; + min-height: 100%; + padding: 20px; + width: 100%; +} + +.card-box { + background-color: rgb(250, 253, 258); + border-radius: 5px; + box-shadow: rgba(60, 66, 87, 0.117647) 0px 7px 14px 0px, rgba(0, 0, 0, 0.117647) 0px 3px 6px 0px; + max-width: 650px; + width: 100%; +} + +.card-box > .header { + border-bottom: 1px solid #ddd; + color: #444; + padding: 30px; +} + +.card-box > .header > .main { + margin: 0; + font-size: 1.5rem; +} + +.card-box > .header > .sub { + color: #555; + margin-top: 10px; +} + +.card-box > .content { + padding: 40px; +} + +.card-box + .card-box { + margin-top: 26px; +} diff --git a/src/browser/pages/home.css b/src/browser/pages/home.css new file mode 100644 index 000000000..8f3cb1195 --- /dev/null +++ b/src/browser/pages/home.css @@ -0,0 +1,47 @@ +.block-row { + display: flex; +} + +.block-row > .item { + flex: 1; + margin: 2px 0; +} + +.block-row > .item.-row { + display: flex; +} + +.block-row > .item > .sub { + font-size: 0.95em; +} + +.block-row .-link { + color: rgb(87, 114, 245); + display: block; + text-decoration: none; +} + +.block-row .-link:hover { + text-decoration: underline; +} + +.block-row > .item > .icon { + height: 1rem; + margin-right: 5px; + vertical-align: top; + width: 1rem; +} + +.block-row > .item > .icon.-missing { + background-color: rgba(87, 114, 245, 0.2); + text-align: center; +} + +.kill-form { + display: inline-block; +} + +.kill-form > .kill { + border-radius: 3px; + padding: 2px 5px; +} diff --git a/src/browser/pages/home.html b/src/browser/pages/home.html new file mode 100644 index 000000000..ef2eff148 --- /dev/null +++ b/src/browser/pages/home.html @@ -0,0 +1,75 @@ + + + + + + + code-server + + + + + + + +
+
+
+

Running

+
Currently running applications.
+
+
+ {{APP_LIST:RUNNING}} +
+
+ +
+
+

Recent

+
Choose a recent directory or workspace to launch below.
+
+
+ {{APP_LIST:RECENT_PROJECTS}} +
+
+ +
+
+

Editors

+
Choose an editor to launch below.
+
+
+ {{APP_LIST:EDITORS}} +
+
+ + + + + + + + + + + +
+
+

Version

+
Version information and updates.
+
+
+ {{UPDATE:NAME}} +
+
+
+ + + diff --git a/src/browser/pages/login.css b/src/browser/pages/login.css new file mode 100644 index 000000000..43d03e7ee --- /dev/null +++ b/src/browser/pages/login.css @@ -0,0 +1,39 @@ +body { + overflow: auto; +} + +.login-form { + display: flex; + flex-direction: column; + flex: 1; + justify-content: center; +} + +.login-form > .field { + display: flex; + flex-direction: row; + width: 100%; +} + +.login-form > .error { + color: red; + margin-top: 16px; +} + +.login-form > .field > .password { + background-color: rgb(244, 247, 252); + border-radius: 5px; + border: 1px solid #ddd; + box-sizing: border-box; + color: black; + flex: 1; + padding: 16px; +} + +.login-form > .user { + display: none; +} + +.login-form > .field > .submit { + margin-left: 20px; +} diff --git a/src/browser/pages/login.html b/src/browser/pages/login.html new file mode 100644 index 000000000..68a339db5 --- /dev/null +++ b/src/browser/pages/login.html @@ -0,0 +1,60 @@ + + + + + + + code-server login + + + + + + + +
+
+
+

Welcome to code-server

+
Please log in below. Check code-server's logs for the generated password.
+
+
+ +
+
+
+ + + + diff --git a/src/browser/pages/update.css b/src/browser/pages/update.css new file mode 100644 index 000000000..eaa8feec8 --- /dev/null +++ b/src/browser/pages/update.css @@ -0,0 +1,26 @@ +.update-form { + text-align: center; +} + +.update-form > .cancel { + background-color: red; +} + +.update-form > .error { + color: red; + margin-top: 16px; +} + +.update-form > .links { + margin-top: 20px; +} + +.update-form > .links > .link { + color: rgb(87, 114, 245); + text-align: center; + text-decoration: none; +} + +.update-form > .links > .link:hover { + text-decoration: underline; +} diff --git a/src/browser/pages/update.html b/src/browser/pages/update.html new file mode 100644 index 000000000..bcac2caa2 --- /dev/null +++ b/src/browser/pages/update.html @@ -0,0 +1,40 @@ + + + + + + + code-server + + + + + + + +
+
+
+

Update

+
Update code-server.
+
+
+
+ {{UPDATE_STATUS}} {{ERROR}} + +
+
+
+
+ + + diff --git a/src/browser/pages/vscode.html b/src/browser/pages/vscode.html new file mode 100644 index 000000000..acead2e4e --- /dev/null +++ b/src/browser/pages/vscode.html @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/browser/register.ts b/src/browser/register.ts new file mode 100644 index 000000000..1faff4bb6 --- /dev/null +++ b/src/browser/register.ts @@ -0,0 +1,14 @@ +import { getOptions, normalize } from "../common/util" + +const options = getOptions() + +if ("serviceWorker" in navigator) { + const path = normalize(`${options.base}/static/${options.commit}/dist/serviceWorker.js`) + navigator.serviceWorker + .register(path, { + scope: options.base || "/", + }) + .then(function() { + console.log("[Service Worker] registered") + }) +} diff --git a/src/browser/serviceWorker.ts b/src/browser/serviceWorker.ts new file mode 100644 index 000000000..b7f2e9c59 --- /dev/null +++ b/src/browser/serviceWorker.ts @@ -0,0 +1,24 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +self.addEventListener("install", () => { + console.log("[Service Worker] install") +}) + +self.addEventListener("activate", (event: any) => { + event.waitUntil((self as any).clients.claim()) +}) + +self.addEventListener("fetch", (event: any) => { + if (!navigator.onLine) { + event.respondWith( + new Promise((resolve) => { + resolve( + new Response("OFFLINE", { + status: 200, + statusText: "OK", + }), + ) + }), + ) + } +}) diff --git a/src/browser/socket.ts b/src/browser/socket.ts new file mode 100644 index 000000000..764f8c90f --- /dev/null +++ b/src/browser/socket.ts @@ -0,0 +1,208 @@ +import { field, logger, Logger } from "@coder/logger" +import { Emitter } from "../common/emitter" +import { generateUuid } from "../common/util" + +const decoder = new TextDecoder("utf8") +export const decode = (buffer: string | ArrayBuffer): string => { + return typeof buffer !== "string" ? decoder.decode(buffer) : buffer +} + +/** + * A web socket that reconnects itself when it closes. Sending messages while + * disconnected will throw an error. + */ +export class ReconnectingSocket { + protected readonly _onMessage = new Emitter() + public readonly onMessage = this._onMessage.event + protected readonly _onDisconnect = new Emitter() + public readonly onDisconnect = this._onDisconnect.event + protected readonly _onClose = new Emitter() + public readonly onClose = this._onClose.event + protected readonly _onConnect = new Emitter() + public readonly onConnect = this._onConnect.event + + // This helps distinguish messages between sockets. + private readonly logger: Logger + + private socket?: WebSocket + private connecting?: Promise + private closed = false + private readonly openTimeout = 10000 + + // Every time the socket fails to connect, the retry will be increasingly + // delayed up to a maximum. + private readonly retryBaseDelay = 1000 + private readonly retryMaxDelay = 10000 + private retryDelay?: number + private readonly retryDelayFactor = 1.5 + + // The socket must be connected for this amount of time before resetting the + // retry delay. This prevents rapid retries when the socket does connect but + // is closed shortly after. + private resetRetryTimeout?: NodeJS.Timeout + private readonly resetRetryDelay = 10000 + + private _binaryType: typeof WebSocket.prototype.binaryType = "arraybuffer" + + public constructor(private customPath?: string, public readonly id: string = generateUuid(4)) { + // On Firefox the socket seems to somehow persist a page reload so the close + // event runs and we see "attempting to reconnect". + if (typeof window !== "undefined") { + window.addEventListener("beforeunload", () => this.close()) + } + this.logger = logger.named(this.id) + } + + public set binaryType(b: typeof WebSocket.prototype.binaryType) { + this._binaryType = b + if (this.socket) { + this.socket.binaryType = b + } + } + + /** + * Permanently close the connection. Will not attempt to reconnect. Will + * remove event listeners. + */ + public close(code?: number): void { + if (this.closed) { + return + } + + if (code) { + this.logger.info(`closing with code ${code}`) + } + + if (this.resetRetryTimeout) { + clearTimeout(this.resetRetryTimeout) + } + + this.closed = true + + if (this.socket) { + this.socket.close() + } else { + this._onClose.emit(code) + } + } + + public dispose(): void { + this._onMessage.dispose() + this._onDisconnect.dispose() + this._onClose.dispose() + this._onConnect.dispose() + this.logger.debug("disposed handlers") + } + + /** + * Send a message on the socket. Logs an error if currently disconnected. + */ + public send(message: string | ArrayBuffer): void { + this.logger.trace(() => ["sending message", field("message", decode(message))]) + if (!this.socket) { + return logger.error("tried to send message on closed socket") + } + this.socket.send(message) + } + + /** + * Connect to the socket. Can also be called to wait until the connection is + * established in the case of disconnections. Multiple calls will be handled + * correctly. + */ + public async connect(): Promise { + if (!this.connecting) { + this.connecting = new Promise((resolve, reject) => { + const tryConnect = (): void => { + if (this.closed) { + return reject(new Error("disconnected")) // Don't keep trying if we've closed permanently. + } + if (typeof this.retryDelay === "undefined") { + this.retryDelay = 0 + } else { + this.retryDelay = this.retryDelay * this.retryDelayFactor || this.retryBaseDelay + if (this.retryDelay > this.retryMaxDelay) { + this.retryDelay = this.retryMaxDelay + } + } + this._connect() + .then((socket) => { + this.logger.info("connected") + this.socket = socket + this.socket.binaryType = this._binaryType + if (this.resetRetryTimeout) { + clearTimeout(this.resetRetryTimeout) + } + this.resetRetryTimeout = setTimeout(() => (this.retryDelay = undefined), this.resetRetryDelay) + this.connecting = undefined + this._onConnect.emit() + resolve() + }) + .catch((error) => { + this.logger.error(`failed to connect: ${error.message}`) + tryConnect() + }) + } + tryConnect() + }) + } + return this.connecting + } + + private async _connect(): Promise { + const socket = await new Promise((resolve, _reject) => { + if (this.retryDelay) { + this.logger.info(`retrying in ${this.retryDelay}ms...`) + } + setTimeout(() => { + this.logger.info("connecting...") + const socket = new WebSocket( + `${location.protocol === "https:" ? "wss" : "ws"}://${location.host}${this.customPath || location.pathname}${ + location.search ? `?${location.search}` : "" + }`, + ) + + const reject = (): void => { + _reject(new Error("socket closed")) + } + + const timeout = setTimeout(() => { + // eslint-disable-next-line @typescript-eslint/no-use-before-define + socket.removeEventListener("open", open) + socket.removeEventListener("close", reject) + _reject(new Error("timeout")) + }, this.openTimeout) + + const open = (): void => { + clearTimeout(timeout) + socket.removeEventListener("close", reject) + resolve(socket) + } + + socket.addEventListener("open", open) + socket.addEventListener("close", reject) + }, this.retryDelay) + }) + + socket.addEventListener("message", (event) => { + this.logger.trace(() => ["got message", field("message", decode(event.data))]) + this._onMessage.emit(event.data) + }) + socket.addEventListener("close", (event) => { + this.socket = undefined + if (!this.closed) { + this._onDisconnect.emit(event.code) + // It might be closed in the event handler. + if (!this.closed) { + this.logger.info("connection closed; attempting to reconnect") + this.connect() + } + } else { + this._onClose.emit(event.code) + this.logger.info("connection closed permanently") + } + }) + + return socket + } +} diff --git a/src/browser/workbench-build.html b/src/browser/workbench-build.html deleted file mode 100644 index 014cc1f34..000000000 --- a/src/browser/workbench-build.html +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/browser/workbench.html b/src/browser/workbench.html deleted file mode 100644 index 4e566bdca..000000000 --- a/src/browser/workbench.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/browser/worker.ts b/src/browser/worker.ts deleted file mode 100644 index ed7ea6c96..000000000 --- a/src/browser/worker.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { URI } from "vs/base/common/uri"; -import { IExtensionDescription } from "vs/platform/extensions/common/extensions"; -import { ILogService } from "vs/platform/log/common/log"; -import { Client } from "vs/server/node_modules/@coder/node-browser/out/client/client"; -import { fromTar } from "vs/server/node_modules/@coder/requirefs/out/requirefs"; -import { ExtensionActivationTimesBuilder } from "vs/workbench/api/common/extHostExtensionActivator"; -import { IExtHostNodeProxy } from "./extHostNodeProxy"; - -export const loadCommonJSModule = async ( - module: IExtensionDescription, - activationTimesBuilder: ExtensionActivationTimesBuilder, - nodeProxy: IExtHostNodeProxy, - logService: ILogService, - vscode: any, -): Promise => { - const fetchUri = URI.from({ - scheme: self.location.protocol.replace(":", ""), - authority: self.location.host, - path: `${self.location.pathname.replace(/\/static.*\/out\/vs\/workbench\/services\/extensions\/worker\/extensionHostWorkerMain.js$/, "")}/tar`, - query: `path=${encodeURIComponent(module.extensionLocation.path)}`, - }); - const response = await fetch(fetchUri.toString(true)); - if (response.status !== 200) { - throw new Error(`Failed to download extension "${module.extensionLocation.path}"`); - } - const client = new Client(nodeProxy, { logger: logService }); - const init = await client.handshake(); - const buffer = new Uint8Array(await response.arrayBuffer()); - const rfs = fromTar(buffer); - (self).global = self; - rfs.provide("vscode", vscode); - Object.keys(client.modules).forEach((key) => { - const mod = (client.modules as any)[key]; - if (key === "process") { - (self).process = mod; - (self).process.env = init.env; - return; - } - - rfs.provide(key, mod); - switch (key) { - case "buffer": - (self).Buffer = mod.Buffer; - break; - case "timers": - (self).setImmediate = mod.setImmediate; - break; - } - }); - - try { - activationTimesBuilder.codeLoadingStart(); - return rfs.require("."); - } finally { - activationTimesBuilder.codeLoadingStop(); - } -}; diff --git a/src/common/api.ts b/src/common/api.ts new file mode 100644 index 000000000..a338bb3c9 --- /dev/null +++ b/src/common/api.ts @@ -0,0 +1,59 @@ +export interface Application { + readonly categories?: string[] + readonly comment?: string + readonly directory?: string + readonly exec?: string + readonly genericName?: string + readonly icon?: string + readonly installed?: boolean + readonly name: string + readonly path?: string + readonly sessionId?: string + readonly version?: string +} + +export interface ApplicationsResponse { + readonly applications: ReadonlyArray +} + +export enum SessionError { + NotFound = 4000, + FailedToStart, + Starting, + InvalidState, + Unknown, +} + +export interface SessionResponse { + /** + * Whether the session was created or an existing one was returned. + */ + created: boolean + sessionId: string +} + +export interface RecentResponse { + readonly paths: string[] + readonly workspaces: string[] +} + +export interface RunningResponse { + readonly applications: ReadonlyArray +} + +export interface HealthRequest { + readonly event: "health" +} + +export type ClientMessage = HealthRequest + +export interface HealthResponse { + readonly event: "health" + readonly connections: number +} + +export type ServerMessage = HealthResponse + +export interface ReadyMessage { + protocol: string +} diff --git a/src/common/emitter.ts b/src/common/emitter.ts new file mode 100644 index 000000000..e30e05435 --- /dev/null +++ b/src/common/emitter.ts @@ -0,0 +1,40 @@ +export interface Disposable { + dispose(): void +} + +export interface Event { + (listener: (value: T) => void): Disposable +} + +/** + * Emitter typecasts for a single event type. + */ +export class Emitter { + private listeners: Array<(value: T) => void> = [] + + public get event(): Event { + return (cb: (value: T) => void): Disposable => { + this.listeners.push(cb) + + return { + dispose: (): void => { + const i = this.listeners.indexOf(cb) + if (i !== -1) { + this.listeners.splice(i, 1) + } + }, + } + } + } + + /** + * Emit an event with a value. + */ + public emit(value: T): void { + this.listeners.forEach((cb) => cb(value)) + } + + public dispose(): void { + this.listeners = [] + } +} diff --git a/src/common/http.ts b/src/common/http.ts new file mode 100644 index 000000000..7d286aa0d --- /dev/null +++ b/src/common/http.ts @@ -0,0 +1,25 @@ +export enum HttpCode { + Ok = 200, + Redirect = 302, + NotFound = 404, + BadRequest = 400, + Unauthorized = 401, + LargePayload = 413, + ServerError = 500, +} + +export class HttpError extends Error { + public constructor(message: string, public readonly code: number) { + super(message) + this.name = this.constructor.name + } +} + +export enum ApiEndpoint { + applications = "/applications", + recent = "/recent", + run = "/run", + running = "/running", + session = "/session", + status = "/status", +} diff --git a/src/common/nodeProxy.ts b/src/common/nodeProxy.ts deleted file mode 100644 index bfbd6575d..000000000 --- a/src/common/nodeProxy.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Event } from "vs/base/common/event"; -import { IChannel, IServerChannel } from "vs/base/parts/ipc/common/ipc"; -import { createDecorator } from "vs/platform/instantiation/common/instantiation"; -import { ReadWriteConnection } from "vs/server/node_modules/@coder/node-browser/out/common/connection"; - -export const INodeProxyService = createDecorator("nodeProxyService"); - -export interface INodeProxyService extends ReadWriteConnection { - _serviceBrand: any; - send(message: string): void; - onMessage: Event; - onUp: Event; - onClose: Event; - onDown: Event; -} - -export class NodeProxyChannel implements IServerChannel { - constructor(private service: INodeProxyService) {} - - listen(_: unknown, event: string): Event { - switch (event) { - case "onMessage": return this.service.onMessage; - } - throw new Error(`Invalid listen ${event}`); - } - - async call(_: unknown, command: string, args?: any): Promise { - switch (command) { - case "send": return this.service.send(args[0]); - } - throw new Error(`Invalid call ${command}`); - } -} - -export class NodeProxyChannelClient { - _serviceBrand: any; - - public readonly onMessage: Event; - - constructor(private readonly channel: IChannel) { - this.onMessage = this.channel.listen("onMessage"); - } - - public send(data: string): void { - this.channel.call("send", [data]); - } -} diff --git a/src/common/telemetry.ts b/src/common/telemetry.ts deleted file mode 100644 index 28adaed58..000000000 --- a/src/common/telemetry.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { ITelemetryData } from "vs/base/common/actions"; -import { Event } from "vs/base/common/event"; -import { IChannel, IServerChannel } from "vs/base/parts/ipc/common/ipc"; -import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from "vs/platform/telemetry/common/gdprTypings"; -import { ITelemetryInfo, ITelemetryService } from "vs/platform/telemetry/common/telemetry"; - -export class TelemetryChannel implements IServerChannel { - constructor(private service: ITelemetryService) {} - - listen(_: unknown, event: string): Event { - throw new Error(`Invalid listen ${event}`); - } - - call(_: unknown, command: string, args?: any): Promise { - switch (command) { - case "publicLog": return this.service.publicLog(args[0], args[1], args[2]); - case "publicLog2": return this.service.publicLog2(args[0], args[1], args[2]); - case "setEnabled": return Promise.resolve(this.service.setEnabled(args[0])); - case "getTelemetryInfo": return this.service.getTelemetryInfo(); - } - throw new Error(`Invalid call ${command}`); - } -} - -export class TelemetryChannelClient implements ITelemetryService { - _serviceBrand: any; - - constructor(private readonly channel: IChannel) {} - - public publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): Promise { - return this.channel.call("publicLog", [eventName, data, anonymizeFilePaths]); - } - - public publicLog2 = never, T extends GDPRClassification = never>(eventName: string, data?: StrictPropertyCheck, anonymizeFilePaths?: boolean): Promise { - return this.channel.call("publicLog2", [eventName, data, anonymizeFilePaths]); - } - - public setEnabled(value: boolean): void { - this.channel.call("setEnable", [value]); - } - - public getTelemetryInfo(): Promise { - return this.channel.call("getTelemetryInfo"); - } - - public get isOptedIn(): boolean { - return true; - } -} diff --git a/src/common/util.ts b/src/common/util.ts index 9be7dbe76..9993d4140 100644 --- a/src/common/util.ts +++ b/src/common/util.ts @@ -1,10 +1,67 @@ +import { logger } from "@coder/logger" + +export interface Options { + base: string + commit: string + logLevel: number + sessionId?: string +} + /** * Split a string up to the delimiter. If the delimiter doesn't exist the first * item will have all the text and the second item will be an empty string. */ export const split = (str: string, delimiter: string): [string, string] => { - const index = str.indexOf(delimiter); - return index !== -1 - ? [str.substring(0, index).trim(), str.substring(index + 1)] - : [str, ""]; -}; + const index = str.indexOf(delimiter) + return index !== -1 ? [str.substring(0, index).trim(), str.substring(index + 1)] : [str, ""] +} + +export const plural = (count: number): string => (count === 1 ? "" : "s") + +export const generateUuid = (length = 24): string => { + const possible = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + return Array(length) + .fill(1) + .map(() => possible[Math.floor(Math.random() * possible.length)]) + .join("") +} + +/** + * Remove extra slashes in a URL. + */ +export const normalize = (url: string, keepTrailing = false): string => { + return url.replace(/\/\/+/g, "/").replace(/\/+$/, keepTrailing ? "/" : "") +} + +/** + * Get options embedded in the HTML from the server. + */ +export const getOptions = (): T => { + if (typeof document === "undefined") { + return {} as T + } + const el = document.getElementById("coder-options") + try { + if (!el) { + throw new Error("no options element") + } + const value = el.getAttribute("data-settings") + if (!value) { + throw new Error("no options value") + } + const options = JSON.parse(value) + if (typeof options.logLevel !== "undefined") { + logger.level = options.logLevel + } + const parts = window.location.pathname.replace(/^\//g, "").split("/") + parts[parts.length - 1] = options.base + const url = new URL(window.location.origin + "/" + parts.join("/")) + return { + ...options, + base: normalize(url.pathname, true), + } + } catch (error) { + logger.warn(error.message) + return {} as T + } +} diff --git a/src/media/code-server.png b/src/media/code-server.png deleted file mode 100644 index d3271a30c..000000000 Binary files a/src/media/code-server.png and /dev/null differ diff --git a/src/media/favicon.ico b/src/media/favicon.ico deleted file mode 100644 index be99fc8c5..000000000 Binary files a/src/media/favicon.ico and /dev/null differ diff --git a/src/media/login.css b/src/media/login.css deleted file mode 100644 index f075a6386..000000000 --- a/src/media/login.css +++ /dev/null @@ -1,94 +0,0 @@ -html { - box-sizing: border-box; -} - -*, *:before, *:after { - box-sizing: inherit; -} - -html, body { - background-color: #FFFFFF; - height: 100%; - min-height: 100%; -} - -body { - align-items: center; - display: flex; - font-family: "monospace"; - justify-content: center; - margin: 0; - padding: 10px; -} - -.login-form { - border-radius: 5px; - box-shadow: 0 18px 80px 10px rgba(69, 65, 78, 0.08); - color: #575962; - margin-top: -10%; - max-width: 328px; - padding: 40px; - position: relative; - width: 100%; -} - -.login-form > .title { - text-align: center; - text-transform: uppercase; - font-size: 12px; - font-weight: 500; - letter-spacing: 1.5px; - line-height: 15px; - margin-bottom: 0px; - margin-bottom: 5px; - margin-top: 0px; -} - -.login-form > .subtitle { - font-size: 19px; - font-weight: bold; - line-height: 25px; - margin-bottom: 45px; - margin: 0; - text-align: center; -} - -.login-form > .field { - text-align: left; - font-size: 12px; - color: #797E84; - margin: 16px 0; -} - -.login-form > .field > .input { - background: none !important; - border: 1px solid #ccc; - border-radius: 2px; - padding: 5px; - width: 100%; -} - -.login-form > .button { - border: none; - border-radius: 24px; - box-shadow: 0 12px 17px 2px rgba(171,173,163,0.14), 0 5px 22px 4px rgba(171,173,163,0.12), 0 7px 8px -4px rgba(171,173,163,0.2); - cursor: pointer; - display: block; - padding: 15px 5px; - width: 100%; -} - -.login-form > .button:hover { - background-color: rgb(0, 122, 204); - color: #fff; -} - -.error-display { - box-sizing: border-box; - color: #bb2d0f; - font-size: 14px; - font-weight: 400; - line-height: 12px; - padding: 20px 8px 0; - text-align: center; -} diff --git a/src/media/manifest.json b/src/media/manifest.json deleted file mode 100644 index 2495e2ba9..000000000 --- a/src/media/manifest.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "code-server", - "short_name": "code-server", - "start_url": ".", - "display": "standalone", - "background-color": "#fff", - "description": "Run VS Code on a remote server.", - "icons": [{ - "src": "./code-server.png", - "sizes": "384x384", - "type": "image/png" - }] -} diff --git a/src/node/app/README.md b/src/node/app/README.md new file mode 100644 index 000000000..752c2948b --- /dev/null +++ b/src/node/app/README.md @@ -0,0 +1,59 @@ +Implementation of [VS Code](https://code.visualstudio.com/) remote/web for use +in `code-server`. + +## Docker + +To debug Golang in VS Code using the +[ms-vscode-go extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.Go), +you need to add `--security-opt seccomp=unconfined` to your `docker run` +arguments when launching code-server with Docker. See +[#725](https://github.com/cdr/code-server/issues/725) for details. + +## Known Issues + +- Creating custom VS Code extensions and debugging them doesn't work. +- Extension profiling and tips are currently disabled. + +## Extensions + +`code-server` does not provide access to the official +[Visual Studio Marketplace](https://marketplace.visualstudio.com/vscode). Instead, +Coder has created a custom extension marketplace that we manage for open-source +extensions. If you want to use an extension with code-server that we do not have +in our marketplace please look for a release in the extension’s repository, +contact us to see if we have one in the works or, if you build an extension +locally from open source, you can copy it to the `extensions` folder. If you +build one locally from open-source please contribute it to the project and let +us know so we can give you props! If you have your own custom marketplace, it is +possible to point code-server to it by setting the `SERVICE_URL` and `ITEM_URL` +environment variables. + +## Development: upgrading VS Code + +We patch VS Code to provide and fix some functionality. As the web portion of VS +Code matures, we'll be able to shrink and maybe even entirely eliminate our +patch. In the meantime, however, upgrading the VS Code version requires ensuring +that the patch still applies and has the intended effects. + +If functionality doesn't depend on code from VS Code then it should be moved +into code-server otherwise it should be in the patch. + +To generate a new patch, **stage all the changes** you want to be included in +the patch in the VS Code source, then run `yarn patch:generate` in this +directory. + +Our changes include: + +- Allow multiple extension directories (both user and built-in). +- Modify the loader, websocket, webview, service worker, and asset requests to + use the URL of the page as a base (and TLS if necessary for the websocket). +- Send client-side telemetry through the server. +- Make changing the display language work. +- Make it possible for us to load code on the client. +- Make extensions work in the browser. +- Fix getting permanently disconnected when you sleep or hibernate for a while. +- Make it possible to automatically update the binary. + +## Future + +- Run VS Code unit tests against our builds to ensure features work as expected. diff --git a/src/node/app/api.ts b/src/node/app/api.ts new file mode 100644 index 000000000..6615df1ec --- /dev/null +++ b/src/node/app/api.ts @@ -0,0 +1,398 @@ +import { field, logger } from "@coder/logger" +import * as cp from "child_process" +import * as fs from "fs-extra" +import * as http from "http" +import * as net from "net" +import * as path from "path" +import * as url from "url" +import * as WebSocket from "ws" +import { + Application, + ApplicationsResponse, + ClientMessage, + RecentResponse, + RunningResponse, + ServerMessage, + SessionError, + SessionResponse, +} from "../../common/api" +import { ApiEndpoint, HttpCode, HttpError } from "../../common/http" +import { normalize } from "../../common/util" +import { HttpProvider, HttpProviderOptions, HttpResponse, HttpServer, Route } from "../http" +import { findApplications, findWhitelistedApplications, Vscode } from "./bin" +import { VscodeHttpProvider } from "./vscode" + +interface ServerSession { + process?: cp.ChildProcess + readonly app: Application +} + +interface VsRecents { + [key: string]: (string | { configURIPath: string })[] +} + +type VsSettings = [string, string][] + +/** + * API HTTP provider. + */ +export class ApiHttpProvider extends HttpProvider { + private readonly ws = new WebSocket.Server({ noServer: true }) + private readonly sessions = new Map() + + public constructor( + options: HttpProviderOptions, + private readonly server: HttpServer, + private readonly vscode: VscodeHttpProvider, + private readonly dataDir?: string, + ) { + super(options) + } + + public dispose(): void { + this.sessions.forEach((s) => { + if (s.process) { + s.process.kill() + } + }) + } + + public async handleRequest(route: Route, request: http.IncomingMessage): Promise { + this.ensureAuthenticated(request) + if (route.requestPath !== "/index.html") { + throw new HttpError("Not found", HttpCode.NotFound) + } + + switch (route.base) { + case ApiEndpoint.applications: + this.ensureMethod(request) + return { + content: { + applications: await this.applications(), + }, + } as HttpResponse + case ApiEndpoint.session: + return this.session(request) + case ApiEndpoint.recent: + this.ensureMethod(request) + return { + content: await this.recent(), + } as HttpResponse + case ApiEndpoint.running: + this.ensureMethod(request) + return { + content: await this.running(), + } as HttpResponse + } + + throw new HttpError("Not found", HttpCode.NotFound) + } + + public async handleWebSocket( + route: Route, + request: http.IncomingMessage, + socket: net.Socket, + head: Buffer, + ): Promise { + if (!this.authenticated(request)) { + throw new Error("not authenticated") + } + switch (route.base) { + case ApiEndpoint.status: + return this.handleStatusSocket(request, socket, head) + case ApiEndpoint.run: + return this.handleRunSocket(route, request, socket, head) + } + + throw new HttpError("Not found", HttpCode.NotFound) + } + + private async handleStatusSocket(request: http.IncomingMessage, socket: net.Socket, head: Buffer): Promise { + const getMessageResponse = async (event: "health"): Promise => { + switch (event) { + case "health": + return { event, connections: await this.server.getConnections() } + default: + throw new Error("unexpected message") + } + } + + await new Promise((resolve) => { + this.ws.handleUpgrade(request, socket, head, (ws) => { + const send = (event: ServerMessage): void => { + ws.send(JSON.stringify(event)) + } + ws.on("message", (data) => { + logger.trace("got message", field("message", data)) + try { + const message: ClientMessage = JSON.parse(data.toString()) + getMessageResponse(message.event).then(send) + } catch (error) { + logger.error(error.message, field("message", data)) + } + }) + resolve() + }) + }) + + return true + } + + /** + * A socket that connects to a session. + */ + private async handleRunSocket( + route: Route, + request: http.IncomingMessage, + socket: net.Socket, + head: Buffer, + ): Promise { + const sessionId = route.requestPath.replace(/^\//, "") + logger.debug("connecting session", field("sessionId", sessionId)) + const ws = await new Promise((resolve, reject) => { + this.ws.handleUpgrade(request, socket, head, (socket) => { + socket.binaryType = "arraybuffer" + + const session = this.sessions.get(sessionId) + if (!session) { + socket.close(SessionError.NotFound) + return reject(new Error("session not found")) + } + + resolve(socket as WebSocket) + + socket.on("error", (error) => { + socket.close(SessionError.FailedToStart) + logger.error("got error while connecting socket", field("error", error)) + reject(error) + }) + }) + }) + + // Send ready message. + ws.send( + Buffer.from( + JSON.stringify({ + protocol: "TODO", + }), + ), + ) + + return true + } + + /** + * Return whitelisted applications. + */ + public async applications(): Promise> { + return findWhitelistedApplications() + } + + /** + * Return installed applications. + */ + public async installedApplications(): Promise> { + return findApplications() + } + + /** + * Get a running application. + */ + public getRunningApplication(sessionIdOrPath?: string): Application | undefined { + if (!sessionIdOrPath) { + return undefined + } + + const sessionId = sessionIdOrPath.replace(/\//g, "") + let session = this.sessions.get(sessionId) + if (session) { + logger.debug("found application by session id", field("id", sessionId)) + return session.app + } + + const base = normalize("/" + sessionIdOrPath) + session = Array.from(this.sessions.values()).find((s) => s.app.path === base) + if (session) { + logger.debug("found application by path", field("path", base)) + return session.app + } + + logger.debug("no application found matching route", field("route", sessionIdOrPath)) + return undefined + } + + /** + * Handle /session endpoint. + */ + private async session(request: http.IncomingMessage): Promise { + this.ensureMethod(request, ["DELETE", "POST"]) + + const data = await this.getData(request) + if (!data) { + throw new HttpError("Not found", HttpCode.NotFound) + } + + switch (request.method) { + case "DELETE": + return this.deleteSession(JSON.parse(data).sessionId) + case "POST": { + // Prevent spawning the same app multiple times. + const parsed: Application = JSON.parse(data) + const app = this.getRunningApplication(parsed.sessionId || parsed.path) + if (app) { + return { + content: { + created: false, + sessionId: app.sessionId, + }, + } as HttpResponse + } + return { + content: { + created: true, + sessionId: await this.createSession(parsed), + }, + } as HttpResponse + } + } + + throw new HttpError("Not found", HttpCode.NotFound) + } + + /** + * Kill a session identified by `app.sessionId`. + */ + public async deleteSession(sessionId: string): Promise { + logger.debug("deleting session", field("sessionId", sessionId)) + switch (sessionId) { + case "vscode": + await this.vscode.dispose() + return { code: HttpCode.Ok } + default: { + const session = this.sessions.get(sessionId) + if (!session) { + throw new Error("session does not exist") + } + if (session.process) { + session.process.kill() + } + this.sessions.delete(sessionId) + return { code: HttpCode.Ok } + } + } + } + + /** + * Create a new session and return the session ID. + */ + public async createSession(app: Application): Promise { + const sessionId = Math.floor(Math.random() * 10000).toString() + if (this.sessions.has(sessionId)) { + throw new Error("conflicting session id") + } + + if (!app.exec) { + throw new Error("cannot execute application with no `exec`") + } + + const appSession: ServerSession = { + app: { + ...app, + sessionId, + }, + } + this.sessions.set(sessionId, appSession) + + try { + throw new Error("TODO") + } catch (error) { + this.sessions.delete(sessionId) + throw error + } + } + + /** + * Return VS Code's recent paths. + */ + public async recent(): Promise { + try { + if (!this.dataDir) { + throw new Error("data directory is not set") + } + + const state: VsSettings = JSON.parse(await fs.readFile(path.join(this.dataDir, "User/state/global.json"), "utf8")) + const setting = Array.isArray(state) && state.find((item) => item[0] === "recently.opened") + if (!setting) { + throw new Error("settings appear malformed") + } + + const pathPromises: { [key: string]: Promise } = {} + const workspacePromises: { [key: string]: Promise } = {} + Object.values(JSON.parse(setting[1]) as VsRecents).forEach((recents) => { + recents.forEach((recent) => { + try { + const target = typeof recent === "string" ? pathPromises : workspacePromises + const pathname = url.parse(typeof recent === "string" ? recent : recent.configURIPath).pathname + if (pathname && !target[pathname]) { + target[pathname] = new Promise((resolve) => { + fs.stat(pathname) + .then(() => resolve(pathname)) + .catch(() => resolve()) + }) + } + } catch (error) { + logger.debug("invalid path", field("path", recent)) + } + }) + }) + + const [paths, workspaces] = await Promise.all([ + Promise.all(Object.values(pathPromises)), + Promise.all(Object.values(workspacePromises)), + ]) + + return { + paths: paths.filter((p) => !!p), + workspaces: workspaces.filter((p) => !!p), + } + } catch (error) { + if (error.code !== "ENOENT") { + throw error + } + } + + return { paths: [], workspaces: [] } + } + + /** + * Return running sessions. + */ + public async running(): Promise { + return { + applications: (this.vscode.running + ? [ + { + ...Vscode, + sessionId: "vscode", + }, + ] + : [] + ).concat( + Array.from(this.sessions).map(([sessionId, session]) => ({ + ...session.app, + sessionId, + })), + ), + } + } + + /** + * For these, just return the error message since they'll be requested as + * JSON. + */ + public async getErrorRoot(_route: Route, _title: string, _header: string, error: string): Promise { + return { + content: JSON.stringify({ error }), + } + } +} diff --git a/src/node/app/app.ts b/src/node/app/app.ts new file mode 100644 index 000000000..9de8ade2a --- /dev/null +++ b/src/node/app/app.ts @@ -0,0 +1,46 @@ +import * as http from "http" +import { HttpCode, HttpError } from "../../common/http" +import { HttpProvider, HttpProviderOptions, HttpResponse, Route } from "../http" +import { ApiHttpProvider } from "./api" + +/** + * App/fallback HTTP provider. + */ +export class AppHttpProvider extends HttpProvider { + public constructor(options: HttpProviderOptions, private readonly api: ApiHttpProvider) { + super(options) + } + + public async handleRequest(route: Route, request: http.IncomingMessage): Promise { + if (!this.authenticated(request)) { + return { redirect: "/login", query: { to: route.fullPath } } + } + + this.ensureMethod(request) + if (route.requestPath !== "/index.html") { + throw new HttpError("Not found", HttpCode.NotFound) + } + + // Run an existing app, but if it doesn't exist go ahead and start it. + let app = this.api.getRunningApplication(route.base) + let sessionId = app && app.sessionId + if (!app) { + app = (await this.api.installedApplications()).find((a) => a.path === route.base) + if (app && app.exec) { + sessionId = await this.api.createSession(app) + } + } + + if (sessionId) { + return this.getAppRoot(route, (app && app.name) || "", sessionId) + } + + throw new HttpError("Application not found", HttpCode.NotFound) + } + + public async getAppRoot(route: Route, name: string, sessionId: string): Promise { + const response = await this.getUtf8Resource(this.rootPath, "src/browser/pages/app.html") + response.content = response.content.replace(/{{APP_NAME}}/, name) + return this.replaceTemplates(route, response, sessionId) + } +} diff --git a/src/node/app/bin.ts b/src/node/app/bin.ts new file mode 100644 index 000000000..f12ce3a27 --- /dev/null +++ b/src/node/app/bin.ts @@ -0,0 +1,30 @@ +import * as fs from "fs" +import * as path from "path" +import { Application } from "../../common/api" + +const getVscodeVersion = (): string => { + try { + return require(path.resolve(__dirname, "../../../lib/vscode/package.json")).version + } catch (error) { + return "unknown" + } +} + +export const Vscode: Application = { + categories: ["Editor"], + icon: fs.readFileSync(path.resolve(__dirname, "../../../lib/vscode/resources/linux/code.png")).toString("base64"), + installed: true, + name: "VS Code", + path: "/", + version: getVscodeVersion(), +} + +export const findApplications = async (): Promise> => { + const apps: Application[] = [Vscode] + + return apps.sort((a, b): number => a.name.localeCompare(b.name)) +} + +export const findWhitelistedApplications = async (): Promise> => { + return [Vscode] +} diff --git a/src/node/app/dashboard.ts b/src/node/app/dashboard.ts new file mode 100644 index 000000000..f8b4df408 --- /dev/null +++ b/src/node/app/dashboard.ts @@ -0,0 +1,157 @@ +import * as http from "http" +import * as querystring from "querystring" +import { Application, RecentResponse } from "../../common/api" +import { HttpCode, HttpError } from "../../common/http" +import { HttpProvider, HttpProviderOptions, HttpResponse, Route } from "../http" +import { ApiHttpProvider } from "./api" +import { Vscode } from "./bin" +import { UpdateHttpProvider } from "./update" + +/** + * Dashboard HTTP provider. + */ +export class DashboardHttpProvider extends HttpProvider { + public constructor( + options: HttpProviderOptions, + private readonly api: ApiHttpProvider, + private readonly update: UpdateHttpProvider, + ) { + super(options) + } + + public async handleRequest(route: Route, request: http.IncomingMessage): Promise { + if (route.requestPath !== "/index.html") { + throw new HttpError("Not found", HttpCode.NotFound) + } + + switch (route.base) { + case "/delete": { + this.ensureAuthenticated(request) + this.ensureMethod(request, "POST") + const data = await this.getData(request) + const p = data ? querystring.parse(data) : {} + this.api.deleteSession(p.sessionId as string) + return { redirect: this.options.base } + } + + case "/": { + this.ensureMethod(request) + if (!this.authenticated(request)) { + return { redirect: "/login", query: { to: this.options.base } } + } + return this.getRoot(route) + } + } + + throw new HttpError("Not found", HttpCode.NotFound) + } + + public async getRoot(route: Route): Promise { + const base = this.base(route) + const apps = await this.api.installedApplications() + const response = await this.getUtf8Resource(this.rootPath, "src/browser/pages/home.html") + response.content = response.content + .replace(/{{UPDATE:NAME}}/, await this.getUpdate(base)) + .replace(/{{APP_LIST:RUNNING}}/, this.getAppRows(base, (await this.api.running()).applications)) + .replace(/{{APP_LIST:RECENT_PROJECTS}}/, this.getRecentProjectRows(base, await this.api.recent())) + .replace( + /{{APP_LIST:EDITORS}}/, + this.getAppRows( + base, + apps.filter((app) => app.categories && app.categories.includes("Editor")), + ), + ) + .replace( + /{{APP_LIST:OTHER}}/, + this.getAppRows( + base, + apps.filter((app) => !app.categories || !app.categories.includes("Editor")), + ), + ) + return this.replaceTemplates(route, response) + } + + private getRecentProjectRows(base: string, recents: RecentResponse): string { + return recents.paths.length > 0 || recents.workspaces.length > 0 + ? recents.paths.map((recent) => this.getRecentProjectRow(base, recent)).join("\n") + + recents.workspaces.map((recent) => this.getRecentProjectRow(base, recent, true)).join("\n") + : `
No recent directories or workspaces.
` + } + + private getRecentProjectRow(base: string, recent: string, workspace?: boolean): string { + return `` + } + + private getAppRows(base: string, apps: ReadonlyArray): string { + return apps.length > 0 + ? apps.map((app) => this.getAppRow(base, app)).join("\n") + : `
No applications are currently running.
` + } + + private getAppRow(base: string, app: Application): string { + return `
+ + ${ + app.icon + ? `` + : `
` + } +
${app.name}
+
+ ${ + app.sessionId + ? `
+ + +
` + : "" + } +
` + } + + private async getUpdate(base: string): Promise { + if (!this.update.enabled) { + return `
Updates are disabled
` + } + + const humanize = (time: number): string => { + const d = new Date(time) + const pad = (t: number): string => (t < 10 ? "0" : "") + t + return ( + `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}` + + ` ${pad(d.getHours())}:${pad(d.getMinutes())}` + ) + } + + const update = await this.update.getUpdate() + if (this.update.isLatestVersion(update)) { + return `
+
+ Latest: ${update.version} +
Up to date
+
+
+ ${humanize(update.checked)} + Check now +
+
Current: ${this.update.currentVersion}
+
` + } + + return `
+
+ Latest: ${update.version} +
Out of date
+
+
+ ${humanize(update.checked)} + Update now +
+
Current: ${this.update.currentVersion}
+
` + } +} diff --git a/src/node/app/login.ts b/src/node/app/login.ts new file mode 100644 index 000000000..598b13abe --- /dev/null +++ b/src/node/app/login.ts @@ -0,0 +1,110 @@ +import * as http from "http" +import * as querystring from "querystring" +import { HttpCode, HttpError } from "../../common/http" +import { AuthType, HttpProvider, HttpResponse, Route } from "../http" +import { hash } from "../util" + +interface LoginPayload { + password?: string + /** + * Since we must set a cookie with an absolute path, we need to know the full + * base path. + */ + base?: string +} + +/** + * Login HTTP provider. + */ +export class LoginHttpProvider extends HttpProvider { + public async handleRequest(route: Route, request: http.IncomingMessage): Promise { + if (this.options.auth !== AuthType.Password || route.requestPath !== "/index.html") { + throw new HttpError("Not found", HttpCode.NotFound) + } + switch (route.base) { + case "/": + switch (request.method) { + case "POST": + this.ensureMethod(request, ["GET", "POST"]) + return this.tryLogin(route, request) + default: + this.ensureMethod(request) + if (this.authenticated(request)) { + return { + redirect: (Array.isArray(route.query.to) ? route.query.to[0] : route.query.to) || "/", + query: { to: undefined }, + } + } + return this.getRoot(route) + } + } + + throw new HttpError("Not found", HttpCode.NotFound) + } + + public async getRoot(route: Route, error?: Error): Promise { + const response = await this.getUtf8Resource(this.rootPath, "src/browser/pages/login.html") + response.content = response.content.replace(/{{ERROR}}/, error ? `
${error.message}
` : "") + return this.replaceTemplates(route, response) + } + + /** + * Try logging in. On failure, show the login page with an error. + */ + private async tryLogin(route: Route, request: http.IncomingMessage): Promise { + // Already authenticated via cookies? + const providedPassword = this.authenticated(request) + if (providedPassword) { + return { code: HttpCode.Ok } + } + + try { + const data = await this.getData(request) + const payload = data ? querystring.parse(data) : {} + return await this.login(payload, route, request) + } catch (error) { + return this.getRoot(route, error) + } + } + + /** + * Return a cookie if the user is authenticated otherwise throw an error. + */ + private async login(payload: LoginPayload, route: Route, request: http.IncomingMessage): Promise { + const password = this.authenticated(request, { + key: typeof payload.password === "string" ? [hash(payload.password)] : undefined, + }) + + if (password) { + return { + redirect: (Array.isArray(route.query.to) ? route.query.to[0] : route.query.to) || "/", + query: { to: undefined }, + cookie: + typeof password === "string" + ? { + key: "key", + value: password, + path: payload.base, + } + : undefined, + } + } + + // Only log if it was an actual login attempt. + if (payload && payload.password) { + console.error( + "Failed login attempt", + JSON.stringify({ + xForwardedFor: request.headers["x-forwarded-for"], + remoteAddress: request.connection.remoteAddress, + userAgent: request.headers["user-agent"], + timestamp: Math.floor(new Date().getTime() / 1000), + }), + ) + + throw new Error("Incorrect password") + } + + throw new Error("Missing password") + } +} diff --git a/src/node/app/static.ts b/src/node/app/static.ts new file mode 100644 index 000000000..93be8f400 --- /dev/null +++ b/src/node/app/static.ts @@ -0,0 +1,33 @@ +import * as http from "http" +import { HttpProvider, HttpResponse, Route } from "../http" + +/** + * Static file HTTP provider. Static requests do not require authentication and + * they only allow access to resources within the application. + */ +export class StaticHttpProvider extends HttpProvider { + public async handleRequest(route: Route, request: http.IncomingMessage): Promise { + this.ensureMethod(request) + const response = await this.getReplacedResource(route) + if (!this.isDev) { + response.cache = true + } + return response + } + + /** + * Return a resource with variables replaced where necessary. + */ + protected async getReplacedResource(route: Route): Promise { + // The first part is always the commit (for caching purposes). + const split = route.requestPath.split("/").slice(1) + + switch (split[split.length - 1]) { + case "manifest.json": { + const response = await this.getUtf8Resource(this.rootPath, ...split) + return this.replaceTemplates(route, response) + } + } + return this.getResource(this.rootPath, ...split) + } +} diff --git a/src/node/app/update.ts b/src/node/app/update.ts new file mode 100644 index 000000000..41e1bf5ce --- /dev/null +++ b/src/node/app/update.ts @@ -0,0 +1,368 @@ +import { field, logger } from "@coder/logger" +import zip from "adm-zip" +import * as cp from "child_process" +import * as fs from "fs-extra" +import * as http from "http" +import * as https from "https" +import * as os from "os" +import * as path from "path" +import * as semver from "semver" +import { Readable, Writable } from "stream" +import * as tar from "tar-fs" +import * as url from "url" +import * as util from "util" +import * as zlib from "zlib" +import { HttpCode, HttpError } from "../../common/http" +import { HttpProvider, HttpProviderOptions, HttpResponse, Route } from "../http" +import { settings as globalSettings, SettingsProvider, UpdateSettings } from "../settings" +import { tmpdir } from "../util" +import { ipcMain } from "../wrapper" + +export interface Update { + checked: number + version: string +} + +export interface LatestResponse { + name: string +} + +/** + * Update HTTP provider. + */ +export class UpdateHttpProvider extends HttpProvider { + private update?: Promise + private updateInterval = 1000 * 60 * 60 * 24 // Milliseconds between update checks. + + public constructor( + options: HttpProviderOptions, + public readonly enabled: boolean, + /** + * The URL for getting the latest version of code-server. Should return JSON + * that fulfills `LatestResponse`. + */ + private readonly latestUrl = "https://api.github.com/repos/cdr/code-server/releases/latest", + /** + * The URL for downloading a version of code-server. {{VERSION}} and + * {{RELEASE_NAME}} will be replaced (for example 2.1.0 and + * code-server-2.1.0-linux-x86_64.tar.gz). + */ + private readonly downloadUrl = "https://github.com/cdr/code-server/releases/download/{{VERSION}}/{{RELEASE_NAME}}", + /** + * Update information will be stored here. If not provided, the global + * settings will be used. + */ + private readonly settings: SettingsProvider = globalSettings, + ) { + super(options) + } + + public async handleRequest(route: Route, request: http.IncomingMessage): Promise { + this.ensureAuthenticated(request) + this.ensureMethod(request) + + if (route.requestPath !== "/index.html") { + throw new HttpError("Not found", HttpCode.NotFound) + } + + switch (route.base) { + case "/check": + this.getUpdate(true) + if (route.query && route.query.to) { + return { + redirect: Array.isArray(route.query.to) ? route.query.to[0] : route.query.to, + query: { to: undefined }, + } + } + return this.getRoot(route, request) + case "/apply": + return this.tryUpdate(route, request) + case "/": + return this.getRoot(route, request) + } + + throw new HttpError("Not found", HttpCode.NotFound) + } + + public async getRoot( + route: Route, + request: http.IncomingMessage, + appliedUpdate?: string, + error?: Error, + ): Promise { + if (request.headers["content-type"] === "application/json") { + if (!this.enabled) { + return { + content: { + isLatest: true, + }, + } + } + const update = await this.getUpdate() + return { + content: { + ...update, + isLatest: this.isLatestVersion(update), + }, + } + } + const response = await this.getUtf8Resource(this.rootPath, "src/browser/pages/update.html") + response.content = response.content + .replace(/{{UPDATE_STATUS}}/, appliedUpdate ? `Updated to ${appliedUpdate}` : await this.getUpdateHtml()) + .replace(/{{ERROR}}/, error ? `
${error.message}
` : "") + return this.replaceTemplates(route, response) + } + + /** + * Query for and return the latest update. + */ + public async getUpdate(force?: boolean): Promise { + if (!this.enabled) { + throw new Error("updates are not enabled") + } + + // Don't run multiple requests at a time. + if (!this.update) { + this.update = this._getUpdate(force) + this.update.then(() => (this.update = undefined)) + } + + return this.update + } + + private async _getUpdate(force?: boolean): Promise { + const now = Date.now() + try { + let { update } = !force ? await this.settings.read() : { update: undefined } + if (!update || update.checked + this.updateInterval < now) { + const buffer = await this.request(this.latestUrl) + const data = JSON.parse(buffer.toString()) as LatestResponse + update = { checked: now, version: data.name } + await this.settings.write({ update }) + } + logger.debug("Got latest version", field("latest", update.version)) + return update + } catch (error) { + logger.error("Failed to get latest version", field("error", error.message)) + return { + checked: now, + version: "unknown", + } + } + } + + public get currentVersion(): string { + return require(path.resolve(__dirname, "../../../package.json")).version + } + + /** + * Return true if the currently installed version is the latest. + */ + public isLatestVersion(latest: Update): boolean { + const version = this.currentVersion + logger.debug("Comparing versions", field("current", version), field("latest", latest.version)) + try { + return latest.version === version || semver.lt(latest.version, version) + } catch (error) { + return true + } + } + + private async getUpdateHtml(): Promise { + if (!this.enabled) { + return "Updates are disabled" + } + + const update = await this.getUpdate() + if (this.isLatestVersion(update)) { + return "No update available" + } + + return `` + } + + public async tryUpdate(route: Route, request: http.IncomingMessage): Promise { + try { + const update = await this.getUpdate() + if (!this.isLatestVersion(update)) { + await this.downloadAndApplyUpdate(update) + return this.getRoot(route, request, update.version) + } + return this.getRoot(route, request) + } catch (error) { + return this.getRoot(route, request, undefined, error) + } + } + + public async downloadAndApplyUpdate(update: Update, targetPath?: string, target?: string): Promise { + const releaseName = await this.getReleaseName(update, target) + const url = this.downloadUrl.replace("{{VERSION}}", update.version).replace("{{RELEASE_NAME}}", releaseName) + + let downloadPath = path.join(tmpdir, "updates", releaseName) + fs.mkdirp(path.dirname(downloadPath)) + + const response = await this.requestResponse(url) + + try { + if (downloadPath.endsWith(".tar.gz")) { + downloadPath = await this.extractTar(response, downloadPath) + } else { + downloadPath = await this.extractZip(response, downloadPath) + } + logger.debug("Downloaded update", field("path", downloadPath)) + + // The archive should have a directory inside at the top level with the + // same name as the archive. + const directoryPath = path.join(downloadPath, path.basename(downloadPath)) + await fs.stat(directoryPath) + + if (!targetPath) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + targetPath = path.resolve(__dirname, "../../../") + } + + logger.debug("Replacing files", field("target", targetPath)) + await fs.move(directoryPath, targetPath, { overwrite: true }) + + await fs.remove(downloadPath) + + if (process.send) { + ipcMain().relaunch(update.version) + } + } catch (error) { + response.destroy(error) + throw error + } + } + + private async extractTar(response: Readable, downloadPath: string): Promise { + downloadPath = downloadPath.replace(/\.tar\.gz$/, "") + logger.debug("Extracting tar", field("path", downloadPath)) + + response.pause() + await fs.remove(downloadPath) + + const decompress = zlib.createGunzip() + response.pipe(decompress as Writable) + response.on("error", (error) => decompress.destroy(error)) + response.on("close", () => decompress.end()) + + const destination = tar.extract(downloadPath) + decompress.pipe(destination) + decompress.on("error", (error) => destination.destroy(error)) + decompress.on("close", () => destination.end()) + + await new Promise((resolve, reject) => { + destination.on("finish", resolve) + destination.on("error", reject) + response.resume() + }) + + return downloadPath + } + + private async extractZip(response: Readable, downloadPath: string): Promise { + logger.debug("Downloading zip", field("path", downloadPath)) + + response.pause() + await fs.remove(downloadPath) + + const write = fs.createWriteStream(downloadPath) + response.pipe(write) + response.on("error", (error) => write.destroy(error)) + response.on("close", () => write.end()) + + await new Promise((resolve, reject) => { + write.on("error", reject) + write.on("close", resolve) + response.resume + }) + + const zipPath = downloadPath + downloadPath = downloadPath.replace(/\.zip$/, "") + await fs.remove(downloadPath) + + logger.debug("Extracting zip", field("path", zipPath)) + + await new Promise((resolve, reject) => { + new zip(zipPath).extractAllToAsync(downloadPath, true, (error) => { + return error ? reject(error) : resolve() + }) + }) + + await fs.remove(zipPath) + + return downloadPath + } + + /** + * Given an update return the name for the packaged archived. + */ + private async getReleaseName(update: Update, target: string = os.platform()): Promise { + if (target === "linux") { + const result = await util + .promisify(cp.exec)("ldd --version") + .catch((error) => ({ + stderr: error.message, + stdout: "", + })) + if (/musl/.test(result.stderr) || /musl/.test(result.stdout)) { + target = "alpine" + } + } + let arch = os.arch() + if (arch === "x64") { + arch = "x86_64" + } + return `code-server-${update.version}-${target}-${arch}.${target === "darwin" ? "zip" : "tar.gz"}` + } + + private async request(uri: string): Promise { + const response = await this.requestResponse(uri) + return new Promise((resolve, reject) => { + const chunks: Buffer[] = [] + let bufferLength = 0 + response.on("data", (chunk) => { + bufferLength += chunk.length + chunks.push(chunk) + }) + response.on("error", reject) + response.on("end", () => { + resolve(Buffer.concat(chunks, bufferLength)) + }) + }) + } + + private async requestResponse(uri: string): Promise { + let redirects = 0 + const maxRedirects = 10 + return new Promise((resolve, reject) => { + const request = (uri: string): void => { + logger.debug("Making request", field("uri", uri)) + const httpx = uri.startsWith("https") ? https : http + httpx.get(uri, { headers: { "User-Agent": "code-server" } }, (response) => { + if ( + response.statusCode && + response.statusCode >= 300 && + response.statusCode < 400 && + response.headers.location + ) { + ++redirects + if (redirects > maxRedirects) { + return reject(new Error("reached max redirects")) + } + response.destroy() + return request(url.resolve(uri, response.headers.location)) + } + + if (!response.statusCode || response.statusCode < 200 || response.statusCode >= 400) { + return reject(new Error(`${response.statusCode || "500"}`)) + } + + resolve(response) + }) + } + request(uri) + }) + } +} diff --git a/src/node/app/vscode.ts b/src/node/app/vscode.ts new file mode 100644 index 000000000..f191faf60 --- /dev/null +++ b/src/node/app/vscode.ts @@ -0,0 +1,253 @@ +import { field, logger } from "@coder/logger" +import * as cp from "child_process" +import * as crypto from "crypto" +import * as fs from "fs-extra" +import * as http from "http" +import * as net from "net" +import * as path from "path" +import * as url from "url" +import { + CodeServerMessage, + StartPath, + VscodeMessage, + VscodeOptions, + WorkbenchOptions, +} from "../../../lib/vscode/src/vs/server/ipc" +import { HttpCode, HttpError } from "../../common/http" +import { generateUuid } from "../../common/util" +import { Args } from "../cli" +import { HttpProvider, HttpProviderOptions, HttpResponse, Route } from "../http" +import { settings } from "../settings" + +export class VscodeHttpProvider extends HttpProvider { + private readonly serverRootPath: string + private readonly vsRootPath: string + private _vscode?: Promise + + public constructor(options: HttpProviderOptions, private readonly args: Args) { + super(options) + this.vsRootPath = path.resolve(this.rootPath, "lib/vscode") + this.serverRootPath = path.join(this.vsRootPath, "out/vs/server") + } + + public get running(): boolean { + return !!this._vscode + } + + public async dispose(): Promise { + if (this._vscode) { + const vscode = await this._vscode + vscode.removeAllListeners() + this._vscode = undefined + vscode.kill() + } + } + + private async initialize(options: VscodeOptions): Promise { + const id = generateUuid() + const vscode = await this.fork() + + logger.debug("setting up vs code...") + return new Promise((resolve, reject) => { + vscode.once("message", (message: VscodeMessage) => { + logger.debug("got message from vs code", field("message", message)) + return message.type === "options" && message.id === id + ? resolve(message.options) + : reject(new Error("Unexpected response during initialization")) + }) + vscode.once("error", reject) + vscode.once("exit", (code) => reject(new Error(`VS Code exited unexpectedly with code ${code}`))) + this.send({ type: "init", id, options }, vscode) + }) + } + + private fork(): Promise { + if (!this._vscode) { + logger.debug("forking vs code...") + const vscode = cp.fork(path.join(this.serverRootPath, "fork")) + vscode.on("error", (error) => { + logger.error(error.message) + this._vscode = undefined + }) + vscode.on("exit", (code) => { + logger.error(`VS Code exited unexpectedly with code ${code}`) + this._vscode = undefined + }) + + this._vscode = new Promise((resolve, reject) => { + vscode.once("message", (message: VscodeMessage) => { + logger.debug("got message from vs code", field("message", message)) + return message.type === "ready" + ? resolve(vscode) + : reject(new Error("Unexpected response waiting for ready response")) + }) + vscode.once("error", reject) + vscode.once("exit", (code) => reject(new Error(`VS Code exited unexpectedly with code ${code}`))) + }) + } + + return this._vscode + } + + public async handleWebSocket(route: Route, request: http.IncomingMessage, socket: net.Socket): Promise { + if (!this.authenticated(request)) { + throw new Error("not authenticated") + } + + // VS Code expects a raw socket. It will handle all the web socket frames. + // We just need to handle the initial upgrade. + // This magic value is specified by the websocket spec. + const magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" + const reply = crypto + .createHash("sha1") + .update(request.headers["sec-websocket-key"] + magic) + .digest("base64") + socket.write( + [ + "HTTP/1.1 101 Switching Protocols", + "Upgrade: websocket", + "Connection: Upgrade", + `Sec-WebSocket-Accept: ${reply}`, + ].join("\r\n") + "\r\n\r\n", + ) + + const vscode = await this._vscode + this.send({ type: "socket", query: route.query }, vscode, socket) + return true + } + + private send(message: CodeServerMessage, vscode?: cp.ChildProcess, socket?: net.Socket): void { + if (!vscode || vscode.killed) { + throw new Error("vscode is not running") + } + vscode.send(message, socket) + } + + public async handleRequest(route: Route, request: http.IncomingMessage): Promise { + this.ensureMethod(request) + + switch (route.base) { + case "/": + if (route.requestPath !== "/index.html") { + throw new HttpError("Not found", HttpCode.NotFound) + } else if (!this.authenticated(request)) { + return { redirect: "/login", query: { to: this.options.base } } + } + try { + return await this.getRoot(request, route) + } catch (error) { + const message = `
VS Code failed to load.
${ + this.isDev + ? `
It might not have finished compiling.
` + + `Check for Finished compilation in the output.` + : "" + }

${error}` + return this.getErrorRoot(route, "VS Code failed to load", "500", message) + } + } + + this.ensureAuthenticated(request) + + switch (route.base) { + case "/resource": + case "/vscode-remote-resource": + if (typeof route.query.path === "string") { + return this.getResource(route.query.path) + } + break + case "/tar": + if (typeof route.query.path === "string") { + return this.getTarredResource(request, route.query.path) + } + break + case "/webview": + if (/^\/vscode-resource/.test(route.requestPath)) { + return this.getResource(route.requestPath.replace(/^\/vscode-resource(\/file)?/, "")) + } + return this.getResource(this.vsRootPath, "out/vs/workbench/contrib/webview/browser/pre", route.requestPath) + } + + throw new HttpError("Not found", HttpCode.NotFound) + } + + private async getRoot(request: http.IncomingMessage, route: Route): Promise { + const remoteAuthority = request.headers.host as string + const { lastVisited } = await settings.read() + const startPath = await this.getFirstValidPath( + [ + { url: route.query.workspace, workspace: true }, + { url: route.query.folder, workspace: false }, + this.args._ && this.args._.length > 0 ? { url: this.args._[this.args._.length - 1] } : undefined, + lastVisited, + ], + remoteAuthority, + ) + const [response, options] = await Promise.all([ + await this.getUtf8Resource(this.rootPath, "src/browser/pages/vscode.html"), + this.initialize({ + args: this.args, + remoteAuthority, + startPath, + }), + ]) + + if (startPath) { + settings.write({ + lastVisited: startPath, + }) + } + + if (!this.isDev) { + response.content = response.content.replace(//g, "") + } + + response.content = response.content + .replace(`"{{REMOTE_USER_DATA_URI}}"`, `'${JSON.stringify(options.remoteUserDataUri)}'`) + .replace(`"{{PRODUCT_CONFIGURATION}}"`, `'${JSON.stringify(options.productConfiguration)}'`) + .replace(`"{{WORKBENCH_WEB_CONFIGURATION}}"`, `'${JSON.stringify(options.workbenchWebConfiguration)}'`) + .replace(`"{{NLS_CONFIGURATION}}"`, `'${JSON.stringify(options.nlsConfiguration)}'`) + return this.replaceTemplates(route, response) + } + + /** + * Choose the first valid path. If `workspace` is undefined then either a + * workspace or a directory are acceptable. Otherwise it must be a file if a + * workspace or a directory otherwise. + */ + private async getFirstValidPath( + startPaths: Array<{ url?: string | string[]; workspace?: boolean } | undefined>, + remoteAuthority: string, + ): Promise { + for (let i = 0; i < startPaths.length; ++i) { + const startPath = startPaths[i] + if (!startPath) { + continue + } + const paths = typeof startPath.url === "string" ? [startPath.url] : startPath.url || [] + for (let j = 0; j < paths.length; ++j) { + const uri = url.parse(paths[j]) + try { + if (!uri.pathname) { + throw new Error(`${paths[j]} is not a valid URL`) + } + const stat = await fs.stat(uri.pathname) + if (typeof startPath.workspace === "undefined" || startPath.workspace !== stat.isDirectory()) { + return { + url: url.format({ + protocol: uri.protocol || "vscode-remote", + hostname: remoteAuthority.split(":")[0], + port: remoteAuthority.split(":")[1], + pathname: uri.pathname, + slashes: true, + }), + workspace: !stat.isDirectory(), + } + } + } catch (error) { + logger.warn(error.message) + } + } + } + return undefined + } +} diff --git a/src/node/channel.ts b/src/node/channel.ts deleted file mode 100644 index 4d21b473e..000000000 --- a/src/node/channel.ts +++ /dev/null @@ -1,343 +0,0 @@ -import * as path from "path"; -import { VSBuffer, VSBufferReadableStream } from "vs/base/common/buffer"; -import { Emitter, Event } from "vs/base/common/event"; -import { IDisposable } from "vs/base/common/lifecycle"; -import { OS } from "vs/base/common/platform"; -import { ReadableStreamEventPayload } from "vs/base/common/stream"; -import { URI, UriComponents } from "vs/base/common/uri"; -import { transformOutgoingURIs } from "vs/base/common/uriIpc"; -import { IServerChannel } from "vs/base/parts/ipc/common/ipc"; -import { IDiagnosticInfo } from "vs/platform/diagnostics/common/diagnostics"; -import { IEnvironmentService } from "vs/platform/environment/common/environment"; -import { ExtensionIdentifier, IExtensionDescription } from "vs/platform/extensions/common/extensions"; -import { FileDeleteOptions, FileOpenOptions, FileOverwriteOptions, FileReadStreamOptions, FileType, FileWriteOptions, IStat, IWatchOptions } from "vs/platform/files/common/files"; -import { createReadStream } from "vs/platform/files/common/io"; -import { DiskFileSystemProvider } from "vs/platform/files/node/diskFileSystemProvider"; -import { ILogService } from "vs/platform/log/common/log"; -import product from "vs/platform/product/common/product"; -import { IRemoteAgentEnvironment, RemoteAgentConnectionContext } from "vs/platform/remote/common/remoteAgentEnvironment"; -import { ITelemetryService } from "vs/platform/telemetry/common/telemetry"; -import { INodeProxyService } from "vs/server/src/common/nodeProxy"; -import { getTranslations } from "vs/server/src/node/nls"; -import { getUriTransformer, localRequire } from "vs/server/src/node/util"; -import { IFileChangeDto } from "vs/workbench/api/common/extHost.protocol"; -import { ExtensionScanner, ExtensionScannerInput } from "vs/workbench/services/extensions/node/extensionPoints"; - -/** - * Extend the file provider to allow unwatching. - */ -class Watcher extends DiskFileSystemProvider { - public readonly watches = new Map(); - - public dispose(): void { - this.watches.forEach((w) => w.dispose()); - this.watches.clear(); - super.dispose(); - } - - public _watch(req: number, resource: URI, opts: IWatchOptions): void { - this.watches.set(req, this.watch(resource, opts)); - } - - public unwatch(req: number): void { - this.watches.get(req)!.dispose(); - this.watches.delete(req); - } -} - -export class FileProviderChannel implements IServerChannel, IDisposable { - private readonly provider: DiskFileSystemProvider; - private readonly watchers = new Map(); - - public constructor( - private readonly environmentService: IEnvironmentService, - private readonly logService: ILogService, - ) { - this.provider = new DiskFileSystemProvider(this.logService); - } - - public listen(context: RemoteAgentConnectionContext, event: string, args?: any): Event { - switch (event) { - case "filechange": return this.filechange(context, args[0]); - case "readFileStream": return this.readFileStream(args[0], args[1]); - } - - throw new Error(`Invalid listen "${event}"`); - } - - private filechange(context: RemoteAgentConnectionContext, session: string): Event { - const emitter = new Emitter({ - onFirstListenerAdd: () => { - const provider = new Watcher(this.logService); - this.watchers.set(session, provider); - const transformer = getUriTransformer(context.remoteAuthority); - provider.onDidChangeFile((events) => { - emitter.fire(events.map((event) => ({ - ...event, - resource: transformer.transformOutgoing(event.resource), - }))); - }); - provider.onDidErrorOccur((event) => this.logService.error(event)); - }, - onLastListenerRemove: () => { - this.watchers.get(session)!.dispose(); - this.watchers.delete(session); - }, - }); - - return emitter.event; - } - - private readFileStream(resource: UriComponents, opts: FileReadStreamOptions): Event> { - let fileStream: VSBufferReadableStream | undefined; - const emitter = new Emitter>({ - onFirstListenerAdd: () => { - if (!fileStream) { - fileStream = createReadStream(this.provider, this.transform(resource), { - ...opts, - bufferSize: 64 * 1024, // From DiskFileSystemProvider - }); - fileStream.on("data", (data) => emitter.fire(data)); - fileStream.on("error", (error) => emitter.fire(error)); - fileStream.on("end", () => emitter.fire("end")); - } - }, - onLastListenerRemove: () => fileStream && fileStream.destroy(), - }); - - return emitter.event; - } - - public call(_: unknown, command: string, args?: any): Promise { - switch (command) { - case "stat": return this.stat(args[0]); - case "open": return this.open(args[0], args[1]); - case "close": return this.close(args[0]); - case "read": return this.read(args[0], args[1], args[2]); - case "readFile": return this.readFile(args[0]); - case "write": return this.write(args[0], args[1], args[2], args[3], args[4]); - case "writeFile": return this.writeFile(args[0], args[1], args[2]); - case "delete": return this.delete(args[0], args[1]); - case "mkdir": return this.mkdir(args[0]); - case "readdir": return this.readdir(args[0]); - case "rename": return this.rename(args[0], args[1], args[2]); - case "copy": return this.copy(args[0], args[1], args[2]); - case "watch": return this.watch(args[0], args[1], args[2], args[3]); - case "unwatch": return this.unwatch(args[0], args[1]); - } - - throw new Error(`Invalid call "${command}"`); - } - - public dispose(): void { - this.watchers.forEach((w) => w.dispose()); - this.watchers.clear(); - } - - private async stat(resource: UriComponents): Promise { - return this.provider.stat(this.transform(resource)); - } - - private async open(resource: UriComponents, opts: FileOpenOptions): Promise { - return this.provider.open(this.transform(resource), opts); - } - - private async close(fd: number): Promise { - return this.provider.close(fd); - } - - private async read(fd: number, pos: number, length: number): Promise<[VSBuffer, number]> { - const buffer = VSBuffer.alloc(length); - const bytesRead = await this.provider.read(fd, pos, buffer.buffer, 0, length); - return [buffer, bytesRead]; - } - - private async readFile(resource: UriComponents): Promise { - return VSBuffer.wrap(await this.provider.readFile(this.transform(resource))); - } - - private write(fd: number, pos: number, buffer: VSBuffer, offset: number, length: number): Promise { - return this.provider.write(fd, pos, buffer.buffer, offset, length); - } - - private writeFile(resource: UriComponents, buffer: VSBuffer, opts: FileWriteOptions): Promise { - return this.provider.writeFile(this.transform(resource), buffer.buffer, opts); - } - - private async delete(resource: UriComponents, opts: FileDeleteOptions): Promise { - return this.provider.delete(this.transform(resource), opts); - } - - private async mkdir(resource: UriComponents): Promise { - return this.provider.mkdir(this.transform(resource)); - } - - private async readdir(resource: UriComponents): Promise<[string, FileType][]> { - return this.provider.readdir(this.transform(resource)); - } - - private async rename(resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): Promise { - return this.provider.rename(this.transform(resource), URI.from(target), opts); - } - - private copy(resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): Promise { - return this.provider.copy(this.transform(resource), URI.from(target), opts); - } - - private async watch(session: string, req: number, resource: UriComponents, opts: IWatchOptions): Promise { - this.watchers.get(session)!._watch(req, this.transform(resource), opts); - } - - private async unwatch(session: string, req: number): Promise { - this.watchers.get(session)!.unwatch(req); - } - - private transform(resource: UriComponents): URI { - // Used for walkthrough content. - if (/^\/static[^/]*\//.test(resource.path)) { - return URI.file(this.environmentService.appRoot + resource.path.replace(/^\/static[^/]*\//, "/")); - // Used by the webview service worker to load resources. - } else if (resource.path === "/vscode-resource" && resource.query) { - try { - const query = JSON.parse(resource.query); - if (query.requestResourcePath) { - return URI.file(query.requestResourcePath); - } - } catch (error) { /* Carry on. */ } - } - return URI.from(resource); - } -} - -export class ExtensionEnvironmentChannel implements IServerChannel { - public constructor( - private readonly environment: IEnvironmentService, - private readonly log: ILogService, - private readonly telemetry: ITelemetryService, - private readonly connectionToken: string, - ) {} - - public listen(_: unknown, event: string): Event { - throw new Error(`Invalid listen "${event}"`); - } - - public async call(context: any, command: string, args?: any): Promise { - switch (command) { - case "getEnvironmentData": - return transformOutgoingURIs( - await this.getEnvironmentData(args.language), - getUriTransformer(context.remoteAuthority), - ); - case "getDiagnosticInfo": return this.getDiagnosticInfo(); - case "disableTelemetry": return this.disableTelemetry(); - } - throw new Error(`Invalid call "${command}"`); - } - - private async getEnvironmentData(locale: string): Promise { - return { - pid: process.pid, - connectionToken: this.connectionToken, - appRoot: URI.file(this.environment.appRoot), - appSettingsHome: this.environment.appSettingsHome, - settingsPath: this.environment.machineSettingsHome, - logsPath: URI.file(this.environment.logsPath), - extensionsPath: URI.file(this.environment.extensionsPath!), - extensionHostLogsPath: URI.file(path.join(this.environment.logsPath, "extension-host")), - globalStorageHome: URI.file(this.environment.globalStorageHome), - userHome: URI.file(this.environment.userHome), - extensions: await this.scanExtensions(locale), - os: OS, - }; - } - - private async scanExtensions(locale: string): Promise { - const translations = await getTranslations(locale, this.environment.userDataPath); - - const scanMultiple = (isBuiltin: boolean, isUnderDevelopment: boolean, paths: string[]): Promise => { - return Promise.all(paths.map((path) => { - return ExtensionScanner.scanExtensions(new ExtensionScannerInput( - product.version, - product.commit, - locale, - !!process.env.VSCODE_DEV, - path, - isBuiltin, - isUnderDevelopment, - translations, - ), this.log); - })); - }; - - const scanBuiltin = async (): Promise => { - return scanMultiple(true, false, [this.environment.builtinExtensionsPath, ...this.environment.extraBuiltinExtensionPaths]); - }; - - const scanInstalled = async (): Promise => { - return scanMultiple(false, true, [this.environment.extensionsPath!, ...this.environment.extraExtensionPaths]); - }; - - return Promise.all([scanBuiltin(), scanInstalled()]).then((allExtensions) => { - const uniqueExtensions = new Map(); - allExtensions.forEach((multipleExtensions) => { - multipleExtensions.forEach((extensions) => { - extensions.forEach((extension) => { - const id = ExtensionIdentifier.toKey(extension.identifier); - if (uniqueExtensions.has(id)) { - const oldPath = uniqueExtensions.get(id)!.extensionLocation.fsPath; - const newPath = extension.extensionLocation.fsPath; - this.log.warn(`${oldPath} has been overridden ${newPath}`); - } - uniqueExtensions.set(id, extension); - }); - }); - }); - return Array.from(uniqueExtensions.values()); - }); - } - - private getDiagnosticInfo(): Promise { - throw new Error("not implemented"); - } - - private async disableTelemetry(): Promise { - this.telemetry.setEnabled(false); - } -} - -export class NodeProxyService implements INodeProxyService { - public _serviceBrand = undefined; - - public readonly server: import("@coder/node-browser/out/server/server").Server; - - private readonly _onMessage = new Emitter(); - public readonly onMessage = this._onMessage.event; - private readonly _$onMessage = new Emitter(); - public readonly $onMessage = this._$onMessage.event; - public readonly _onDown = new Emitter(); - public readonly onDown = this._onDown.event; - public readonly _onUp = new Emitter(); - public readonly onUp = this._onUp.event; - - // Unused because the server connection will never permanently close. - private readonly _onClose = new Emitter(); - public readonly onClose = this._onClose.event; - - public constructor() { - // TODO: down/up - const { Server } = localRequire("@coder/node-browser/out/server/server"); - this.server = new Server({ - onMessage: this.$onMessage, - onClose: this.onClose, - onDown: this.onDown, - onUp: this.onUp, - send: (message: string): void => { - this._onMessage.fire(message); - } - }); - } - - public send(message: string): void { - this._$onMessage.fire(message); - } -} diff --git a/src/node/cli.ts b/src/node/cli.ts index e31cd93c9..7b01486a1 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -1,299 +1,259 @@ -import * as cp from "child_process"; -import * as os from "os"; -import * as path from "path"; -import { setUnexpectedErrorHandler } from "vs/base/common/errors"; -import { main as vsCli } from "vs/code/node/cliProcessMain"; -import { validatePaths } from "vs/code/node/paths"; -import { ParsedArgs } from "vs/platform/environment/common/environment"; -import { buildHelpMessage, buildVersionMessage, Option as VsOption, OPTIONS, OptionDescriptions } from "vs/platform/environment/node/argv"; -import { parseMainProcessArgv } from "vs/platform/environment/node/argvHelper"; -import product from "vs/platform/product/common/product"; -import { ipcMain } from "vs/server/src/node/ipc"; -import { enableCustomMarketplace } from "vs/server/src/node/marketplace"; -import { MainServer } from "vs/server/src/node/server"; -import { AuthType, buildAllowedMessage, enumToArray, FormatType, generateCertificate, generatePassword, localRequire, open, unpackExecutables } from "vs/server/src/node/util"; +import * as path from "path" +import { field, logger, Level } from "@coder/logger" +import { Args as VsArgs } from "../../lib/vscode/src/vs/server/ipc" +import { AuthType } from "./http" +import { xdgLocalDir } from "./util" -const { logger } = localRequire("@coder/logger/out/index"); -setUnexpectedErrorHandler((error) => logger.warn(error.message)); - -interface Args extends ParsedArgs { - auth?: AuthType; - "base-path"?: string; - cert?: string; - "cert-key"?: string; - format?: string; - host?: string; - open?: boolean; - port?: string; - socket?: string; +export class Optional { + public constructor(public readonly value?: T) {} } -// @ts-ignore: Force `keyof Args` to work. -interface Option extends VsOption { - id: keyof Args; +export enum LogLevel { + Trace = "trace", + Debug = "debug", + Info = "info", + Warn = "warn", + Error = "error", } -const getArgs = (): Args => { - // Remove options that won't work or don't make sense. - for (let key in OPTIONS) { - switch (key) { - case "add": - case "diff": - case "file-uri": - case "folder-uri": - case "goto": - case "new-window": - case "reuse-window": - case "wait": - case "disable-gpu": - // TODO: pretty sure these don't work but not 100%. - case "prof-startup": - case "inspect-extensions": - case "inspect-brk-extensions": - delete OPTIONS[key]; - break; - } - } +export class OptionalString extends Optional {} - const options = OPTIONS as OptionDescriptions>; - options["base-path"] = { type: "string", cat: "o", description: "Base path of the URL at which code-server is hosted (used for login redirects)." }; - options["cert"] = { type: "string", cat: "o", description: "Path to certificate. If the path is omitted, both this and --cert-key will be generated." }; - options["cert-key"] = { type: "string", cat: "o", description: "Path to the certificate's key if one was provided." }; - options["format"] = { type: "string", cat: "o", description: `Format for the version. ${buildAllowedMessage(FormatType)}.` }; - options["host"] = { type: "string", cat: "o", description: "Host for the server." }; - options["auth"] = { type: "string", cat: "o", description: `The type of authentication to use. ${buildAllowedMessage(AuthType)}.` }; - options["open"] = { type: "boolean", cat: "o", description: "Open in the browser on startup." }; - options["port"] = { type: "string", cat: "o", description: "Port for the main server." }; - options["socket"] = { type: "string", cat: "o", description: "Listen on a socket instead of host:port." }; - - const args = parseMainProcessArgv(process.argv); - if (!args["user-data-dir"]) { - args["user-data-dir"] = path.join(process.env.XDG_DATA_HOME || path.join(os.homedir(), ".local/share"), "code-server"); - } - if (!args["extensions-dir"]) { - args["extensions-dir"] = path.join(args["user-data-dir"], "extensions"); - } - - if (!args.verbose && !args.log && process.env.LOG_LEVEL) { - args.log = process.env.LOG_LEVEL; - } - - return validatePaths(args); -}; - -const startVscode = async (args: Args): Promise => { - const extra = args["_"] || []; - const options = { - auth: args.auth || AuthType.Password, - basePath: args["base-path"], - cert: args.cert, - certKey: args["cert-key"], - openUri: extra.length > 1 ? extra[extra.length - 1] : undefined, - host: args.host, - password: process.env.PASSWORD, - }; - - if (enumToArray(AuthType).filter((t) => t === options.auth).length === 0) { - throw new Error(`'${options.auth}' is not a valid authentication type.`); - } else if (options.auth === "password" && !options.password) { - options.password = await generatePassword(); - } - - if (!options.certKey && typeof options.certKey !== "undefined") { - throw new Error(`--cert-key cannot be blank`); - } else if (options.certKey && !options.cert) { - throw new Error(`--cert-key was provided but --cert was not`); - } if (!options.cert && typeof options.cert !== "undefined") { - const { cert, certKey } = await generateCertificate(); - options.cert = cert; - options.certKey = certKey; - } - - enableCustomMarketplace(); - - const server = new MainServer({ - ...options, - port: typeof args.port !== "undefined" ? parseInt(args.port, 10) : 8080, - socket: args.socket, - }, args); - - const [serverAddress, /* ignore */] = await Promise.all([ - server.listen(), - unpackExecutables(), - ]); - logger.info(`Server listening on ${serverAddress}`); - - if (options.auth === "password" && !process.env.PASSWORD) { - logger.info(` - Password is ${options.password}`); - logger.info(" - To use your own password, set the PASSWORD environment variable"); - if (!args.auth) { - logger.info(" - To disable use `--auth none`"); - } - } else if (options.auth === "password") { - logger.info(" - Using custom password for authentication"); - } else { - logger.info(" - No authentication"); - } - - if (server.protocol === "https") { - logger.info( - args.cert - ? ` - Using provided certificate${args["cert-key"] ? " and key" : ""} for HTTPS` - : ` - Using generated certificate and key for HTTPS`, - ); - } else { - logger.info(" - Not serving HTTPS"); - } - - if (!server.options.socket && args.open) { - // The web socket doesn't seem to work if browsing with 0.0.0.0. - const openAddress = serverAddress.replace(/:\/\/0.0.0.0/, "://localhost"); - await open(openAddress).catch(console.error); - logger.info(` - Opened ${openAddress}`); - } -}; - -const startCli = (args: Args): boolean | Promise => { - if (args.help) { - const executable = `${product.applicationName}${os.platform() === "win32" ? ".exe" : ""}`; - console.log(buildHelpMessage(product.nameLong, executable, product.codeServerVersion, OPTIONS, false)); - return true; - } - - if (args.version) { - if (args.format === "json") { - console.log(JSON.stringify({ - codeServerVersion: product.codeServerVersion, - commit: product.commit, - vscodeVersion: product.version, - })); - } else { - buildVersionMessage(product.codeServerVersion, product.commit).split("\n").map((line) => logger.info(line)); - } - return true; - } - - const shouldSpawnCliProcess = (): boolean => { - return !!args["install-source"] - || !!args["list-extensions"] - || !!args["install-extension"] - || !!args["uninstall-extension"] - || !!args["locate-extension"] - || !!args["telemetry"]; - }; - - if (shouldSpawnCliProcess()) { - enableCustomMarketplace(); - return vsCli(args); - } - - return false; -}; - -export class WrapperProcess { - private process?: cp.ChildProcess; - private started?: Promise; - private currentVersion = product.codeServerVersion; - - public constructor(private readonly args: Args) { - ipcMain.onMessage(async (message) => { - switch (message.type) { - case "relaunch": - logger.info(`Relaunching: ${this.currentVersion} -> ${message.version}`); - this.currentVersion = message.version; - this.started = undefined; - if (this.process) { - this.process.removeAllListeners(); - this.process.kill(); - } - try { - await this.start(); - } catch (error) { - logger.error(error.message); - process.exit(typeof error.code === "number" ? error.code : 1); - } - break; - default: - logger.error(`Unrecognized message ${message}`); - break; - } - }); - } - - public start(): Promise { - if (!this.started) { - const child = this.spawn(); - this.started = ipcMain.handshake(child).then(() => { - child.once("exit", (code) => exit(code!)); - }); - this.process = child; - } - return this.started; - } - - private spawn(): cp.ChildProcess { - // Flags to pass along to the Node binary. We use the environment variable - // since otherwise the code-server binary will swallow them. - const maxMemory = this.args["max-memory"] || 2048; - let nodeOptions = `${process.env.NODE_OPTIONS || ""} ${this.args["js-flags"] || ""}`; - if (!/max_old_space_size=(\d+)/g.exec(nodeOptions)) { - nodeOptions += ` --max_old_space_size=${maxMemory}`; - } - - // If we're using loose files then we need to specify the path. If we're in - // the binary we need to let the binary determine the path (via nbin) since - // it could be different between binaries which presents a problem when - // upgrading (different version numbers or different staging directories). - const isBinary = (global as any).NBIN_LOADED; - return cp.spawn(process.argv[0], process.argv.slice(isBinary ? 2 : 1), { - env: { - ...process.env, - LAUNCH_VSCODE: "true", - NBIN_BYPASS: undefined, - VSCODE_PARENT_PID: process.pid.toString(), - NODE_OPTIONS: nodeOptions, - }, - stdio: ["inherit", "inherit", "inherit", "ipc"], - }); - } +export interface Args extends VsArgs { + readonly auth?: AuthType + readonly cert?: OptionalString + readonly "cert-key"?: string + readonly "disable-updates"?: boolean + readonly "disable-telemetry"?: boolean + readonly help?: boolean + readonly host?: string + readonly json?: boolean + log?: LogLevel + readonly open?: boolean + readonly port?: number + readonly socket?: string + readonly version?: boolean + readonly "list-extensions"?: boolean + readonly "install-extension"?: string[] + readonly "uninstall-extension"?: string[] + readonly locale?: string + readonly _: string[] } -const main = async(): Promise => { - const args = getArgs(); - if (process.env.LAUNCH_VSCODE) { - await ipcMain.handshake(); - return startVscode(args); - } - return startCli(args) || new WrapperProcess(args).start(); -}; - -const exit = process.exit; -process.exit = function (code?: number) { - const err = new Error(`process.exit() was prevented: ${code || "unknown code"}.`); - console.warn(err.stack); -} as (code?: number) => never; - -// Copy the extension host behavior of killing oneself if the parent dies. This -// also exists in bootstrap-fork.js but spawning with that won't work because we -// override process.exit. -if (typeof process.env.VSCODE_PARENT_PID !== "undefined") { - const parentPid = parseInt(process.env.VSCODE_PARENT_PID, 10); - setInterval(() => { - try { - process.kill(parentPid, 0); // Throws an exception if the process doesn't exist anymore. - } catch (e) { - exit(); - } - }, 5000); +interface Option { + type: T + /** + * Short flag for the option. + */ + short?: string + /** + * Whether the option is a path and should be resolved. + */ + path?: boolean + /** + * Description of the option. Leave blank to hide the option. + */ + description?: string } -// It's possible that the pipe has closed (for example if you run code-server -// --version | head -1). Assume that means we're done. -if (!process.stdout.isTTY) { - process.stdout.on("error", () => exit()); +type OptionType = T extends boolean + ? "boolean" + : T extends OptionalString + ? typeof OptionalString + : T extends LogLevel + ? typeof LogLevel + : T extends AuthType + ? typeof AuthType + : T extends number + ? "number" + : T extends string + ? "string" + : T extends string[] + ? "string[]" + : "unknown" + +type Options = { + [P in keyof T]: Option> } -main().catch((error) => { - logger.error(error.message); - exit(typeof error.code === "number" ? error.code : 1); -}); +const options: Options> = { + auth: { type: AuthType, description: "The type of authentication to use." }, + cert: { + type: OptionalString, + path: true, + description: "Path to certificate. Generated if no path is provided.", + }, + "cert-key": { type: "string", path: true, description: "Path to certificate key when using non-generated cert." }, + "disable-updates": { type: "boolean", description: "Disable automatic updates." }, + "disable-telemetry": { type: "boolean", description: "Disable telemetry." }, + host: { type: "string", description: "Host for the HTTP server." }, + help: { type: "boolean", short: "h", description: "Show this output." }, + json: { type: "boolean" }, + open: { type: "boolean", description: "Open in browser on startup. Does not work remotely." }, + port: { type: "number", description: "Port for the HTTP server." }, + socket: { type: "string", path: true, description: "Path to a socket (host and port will be ignored)." }, + version: { type: "boolean", short: "v", description: "Display version information." }, + _: { type: "string[]" }, + + "user-data-dir": { type: "string", path: true, description: "Path to the user data directory." }, + "extensions-dir": { type: "string", path: true, description: "Path to the extensions directory." }, + "builtin-extensions-dir": { type: "string", path: true }, + "extra-extensions-dir": { type: "string[]", path: true }, + "extra-builtin-extensions-dir": { type: "string[]", path: true }, + "list-extensions": { type: "boolean" }, + "install-extension": { type: "string[]" }, + "uninstall-extension": { type: "string[]" }, + + locale: { type: "string" }, + log: { type: LogLevel }, + verbose: { type: "boolean", short: "vvv", description: "Enable verbose logging." }, +} + +export const optionDescriptions = (): string[] => { + const entries = Object.entries(options).filter(([, v]) => !!v.description) + const widths = entries.reduce( + (prev, [k, v]) => ({ + long: k.length > prev.long ? k.length : prev.long, + short: v.short && v.short.length > prev.short ? v.short.length : prev.short, + }), + { short: 0, long: 0 }, + ) + return entries.map( + ([k, v]) => + `${" ".repeat(widths.short - (v.short ? v.short.length : 0))}${v.short ? `-${v.short}` : " "} --${k}${" ".repeat( + widths.long - k.length, + )} ${v.description}${typeof v.type === "object" ? ` [${Object.values(v.type).join(", ")}]` : ""}`, + ) +} + +export const parse = (argv: string[]): Args => { + const args: Args = { _: [] } + let ended = false + + for (let i = 0; i < argv.length; ++i) { + const arg = argv[i] + + // -- signals the end of option parsing. + if (!ended && arg == "--") { + ended = true + continue + } + + // Options start with a dash and require a value if non-boolean. + if (!ended && arg.startsWith("-")) { + let key: keyof Args | undefined + let value: string | undefined + if (arg.startsWith("--")) { + const split = arg.replace(/^--/, "").split("=", 2) + key = split[0] as keyof Args + value = split[1] + } else { + const short = arg.replace(/^-/, "") + const pair = Object.entries(options).find(([, v]) => v.short === short) + if (pair) { + key = pair[0] as keyof Args + } + } + + if (!key || !options[key]) { + throw new Error(`Unknown option ${arg}`) + } + + const option = options[key] + if (option.type === "boolean") { + ;(args[key] as boolean) = true + continue + } + + // Might already have a value if it was the --long=value format. + if (typeof value === "undefined") { + // A value is only valid if it doesn't look like an option. + value = argv[i + 1] && !argv[i + 1].startsWith("-") ? argv[++i] : undefined + } + + if (!value && option.type === OptionalString) { + ;(args[key] as OptionalString) = new OptionalString(value) + continue + } else if (!value) { + throw new Error(`--${key} requires a value`) + } + + if (option.path) { + value = path.resolve(value) + } + + switch (option.type) { + case "string": + ;(args[key] as string) = value + break + case "string[]": + if (!args[key]) { + ;(args[key] as string[]) = [] + } + ;(args[key] as string[]).push(value) + break + case "number": + ;(args[key] as number) = parseInt(value, 10) + if (isNaN(args[key] as number)) { + throw new Error(`--${key} must be a number`) + } + break + case OptionalString: + ;(args[key] as OptionalString) = new OptionalString(value) + break + default: { + if (!Object.values(option.type).find((v) => v === value)) { + throw new Error(`--${key} valid values: [${Object.values(option.type).join(", ")}]`) + } + ;(args[key] as string) = value + break + } + } + + continue + } + + // Everything else goes into _. + args._.push(arg) + } + + logger.debug("parsed command line", field("args", args)) + + // Ensure the environment variable and the flag are synced up. The flag takes + // priority over the environment variable. + if (args.log === LogLevel.Trace || process.env.LOG_LEVEL === LogLevel.Trace || args.verbose) { + args.log = process.env.LOG_LEVEL = LogLevel.Trace + args.verbose = true + } else if (!args.log && process.env.LOG_LEVEL) { + args.log = process.env.LOG_LEVEL as LogLevel + } else if (args.log) { + process.env.LOG_LEVEL = args.log + } + + switch (args.log) { + case LogLevel.Trace: + logger.level = Level.Trace + break + case LogLevel.Debug: + logger.level = Level.Debug + break + case LogLevel.Info: + logger.level = Level.Info + break + case LogLevel.Warn: + logger.level = Level.Warning + break + case LogLevel.Error: + logger.level = Level.Error + break + } + + if (!args["user-data-dir"]) { + args["user-data-dir"] = xdgLocalDir + } + + if (!args["extensions-dir"]) { + args["extensions-dir"] = path.join(args["user-data-dir"], "extensions") + } + + return args +} diff --git a/src/node/connection.ts b/src/node/connection.ts deleted file mode 100644 index 793b02c32..000000000 --- a/src/node/connection.ts +++ /dev/null @@ -1,158 +0,0 @@ -import * as cp from "child_process"; -import { getPathFromAmdModule } from "vs/base/common/amd"; -import { VSBuffer } from "vs/base/common/buffer"; -import { Emitter } from "vs/base/common/event"; -import { ISocket } from "vs/base/parts/ipc/common/ipc.net"; -import { NodeSocket } from "vs/base/parts/ipc/node/ipc.net"; -import { IEnvironmentService } from "vs/platform/environment/common/environment"; -import { ILogService } from "vs/platform/log/common/log"; -import { getNlsConfiguration } from "vs/server/src/node/nls"; -import { Protocol } from "vs/server/src/node/protocol"; -import { uriTransformerPath } from "vs/server/src/node/util"; -import { IExtHostReadyMessage } from "vs/workbench/services/extensions/common/extensionHostProtocol"; - -export abstract class Connection { - private readonly _onClose = new Emitter(); - public readonly onClose = this._onClose.event; - private disposed = false; - private _offline: number | undefined; - - public constructor(protected protocol: Protocol, public readonly token: string) {} - - public get offline(): number | undefined { - return this._offline; - } - - public reconnect(socket: ISocket, buffer: VSBuffer): void { - this._offline = undefined; - this.doReconnect(socket, buffer); - } - - public dispose(): void { - if (!this.disposed) { - this.disposed = true; - this.doDispose(); - this._onClose.fire(); - } - } - - protected setOffline(): void { - if (!this._offline) { - this._offline = Date.now(); - } - } - - /** - * Set up the connection on a new socket. - */ - protected abstract doReconnect(socket: ISocket, buffer: VSBuffer): void; - protected abstract doDispose(): void; -} - -/** - * Used for all the IPC channels. - */ -export class ManagementConnection extends Connection { - public constructor(protected protocol: Protocol, token: string) { - super(protocol, token); - protocol.onClose(() => this.dispose()); // Explicit close. - protocol.onSocketClose(() => this.setOffline()); // Might reconnect. - } - - protected doDispose(): void { - this.protocol.sendDisconnect(); - this.protocol.dispose(); - this.protocol.getSocket().end(); - } - - protected doReconnect(socket: ISocket, buffer: VSBuffer): void { - this.protocol.beginAcceptReconnection(socket, buffer); - this.protocol.endAcceptReconnection(); - } -} - -export class ExtensionHostConnection extends Connection { - private process?: cp.ChildProcess; - - public constructor( - locale:string, protocol: Protocol, buffer: VSBuffer, token: string, - private readonly log: ILogService, - private readonly environment: IEnvironmentService, - ) { - super(protocol, token); - this.protocol.dispose(); - this.spawn(locale, buffer).then((p) => this.process = p); - this.protocol.getUnderlyingSocket().pause(); - } - - protected doDispose(): void { - if (this.process) { - this.process.kill(); - } - this.protocol.getSocket().end(); - } - - protected doReconnect(socket: ISocket, buffer: VSBuffer): void { - // This is just to set the new socket. - this.protocol.beginAcceptReconnection(socket, null); - this.protocol.dispose(); - this.sendInitMessage(buffer); - } - - private sendInitMessage(buffer: VSBuffer): void { - const socket = this.protocol.getUnderlyingSocket(); - socket.pause(); - this.process!.send({ // Process must be set at this point. - type: "VSCODE_EXTHOST_IPC_SOCKET", - initialDataChunk: (buffer.buffer as Buffer).toString("base64"), - skipWebSocketFrames: this.protocol.getSocket() instanceof NodeSocket, - }, socket); - } - - private async spawn(locale: string, buffer: VSBuffer): Promise { - const config = await getNlsConfiguration(locale, this.environment.userDataPath); - const proc = cp.fork( - getPathFromAmdModule(require, "bootstrap-fork"), - [ "--type=extensionHost", `--uriTransformerPath=${uriTransformerPath}` ], - { - env: { - ...process.env, - AMD_ENTRYPOINT: "vs/workbench/services/extensions/node/extensionHostProcess", - PIPE_LOGGING: "true", - VERBOSE_LOGGING: "true", - VSCODE_EXTHOST_WILL_SEND_SOCKET: "true", - VSCODE_HANDLES_UNCAUGHT_ERRORS: "true", - VSCODE_LOG_STACK: "false", - VSCODE_LOG_LEVEL: this.environment.verbose ? "trace" : this.environment.log, - VSCODE_NLS_CONFIG: JSON.stringify(config), - }, - silent: true, - }, - ); - - proc.on("error", () => this.dispose()); - proc.on("exit", () => this.dispose()); - if (proc.stdout && proc.stderr) { - proc.stdout.setEncoding("utf8").on("data", (d) => this.log.info("Extension host stdout", d)); - proc.stderr.setEncoding("utf8").on("data", (d) => this.log.error("Extension host stderr", d)); - } - proc.on("message", (event) => { - if (event && event.type === "__$console") { - const severity = (this.log)[event.severity] ? event.severity : "info"; - (this.log)[severity]("Extension host", event.arguments); - } - if (event && event.type === "VSCODE_EXTHOST_DISCONNECTED") { - this.setOffline(); - } - }); - - const listen = (message: IExtHostReadyMessage) => { - if (message.type === "VSCODE_EXTHOST_IPC_READY") { - proc.removeListener("message", listen); - this.sendInitMessage(buffer); - } - }; - - return proc.on("message", listen); - } -} diff --git a/src/node/entry.ts b/src/node/entry.ts new file mode 100644 index 000000000..3d9f6c494 --- /dev/null +++ b/src/node/entry.ts @@ -0,0 +1,149 @@ +import { field, logger } from "@coder/logger" +import * as cp from "child_process" +import * as path from "path" +import { CliMessage } from "../../lib/vscode/src/vs/server/ipc" +import { ApiHttpProvider } from "./app/api" +import { AppHttpProvider } from "./app/app" +import { DashboardHttpProvider } from "./app/dashboard" +import { LoginHttpProvider } from "./app/login" +import { StaticHttpProvider } from "./app/static" +import { UpdateHttpProvider } from "./app/update" +import { VscodeHttpProvider } from "./app/vscode" +import { Args, optionDescriptions, parse } from "./cli" +import { AuthType, HttpServer } from "./http" +import { generateCertificate, generatePassword, hash, open } from "./util" +import { ipcMain, wrap } from "./wrapper" + +const main = async (args: Args): Promise => { + const auth = args.auth || AuthType.Password + const originalPassword = auth === AuthType.Password && (process.env.PASSWORD || (await generatePassword())) + + let commit: string | undefined + try { + commit = require("../../package.json").commit + } catch (error) { + logger.warn(error.message) + } + + // Spawn the main HTTP server. + const options = { + auth, + cert: args.cert ? args.cert.value : undefined, + certKey: args["cert-key"], + commit: commit || "development", + host: args.host || (args.auth === AuthType.Password && typeof args.cert !== "undefined" ? "0.0.0.0" : "localhost"), + password: originalPassword ? hash(originalPassword) : undefined, + port: typeof args.port !== "undefined" ? args.port : process.env.PORT ? parseInt(process.env.PORT, 10) : 8080, + socket: args.socket, + } + + if (!options.cert && args.cert) { + const { cert, certKey } = await generateCertificate() + options.cert = cert + options.certKey = certKey + } else if (args.cert && !args["cert-key"]) { + throw new Error("--cert-key is missing") + } + + const httpServer = new HttpServer(options) + const vscode = httpServer.registerHttpProvider("/", VscodeHttpProvider, args) + const api = httpServer.registerHttpProvider("/api", ApiHttpProvider, httpServer, vscode, args["user-data-dir"]) + const update = httpServer.registerHttpProvider("/update", UpdateHttpProvider, !args["disable-updates"]) + httpServer.registerHttpProvider("/app", AppHttpProvider, api) + httpServer.registerHttpProvider("/login", LoginHttpProvider) + httpServer.registerHttpProvider("/static", StaticHttpProvider) + httpServer.registerHttpProvider("/dashboard", DashboardHttpProvider, api, update) + + ipcMain().onDispose(() => httpServer.dispose()) + + logger.info(`code-server ${require("../../package.json").version}`) + const serverAddress = await httpServer.listen() + logger.info(`Server listening on ${serverAddress}`) + + if (auth === AuthType.Password && !process.env.PASSWORD) { + logger.info(` - Password is ${originalPassword}`) + logger.info(" - To use your own password, set the PASSWORD environment variable") + if (!args.auth) { + logger.info(" - To disable use `--auth none`") + } + } else if (auth === AuthType.Password) { + logger.info(" - Using custom password for authentication") + } else { + logger.info(" - No authentication") + } + + if (httpServer.protocol === "https") { + logger.info( + typeof args.cert === "string" + ? ` - Using provided certificate and key for HTTPS` + : ` - Using generated certificate and key for HTTPS`, + ) + } else { + logger.info(" - Not serving HTTPS") + } + + logger.info(` - Automatic updates are ${update.enabled ? "enabled" : "disabled"}`) + + if (serverAddress && !options.socket && args.open) { + // The web socket doesn't seem to work if browsing with 0.0.0.0. + const openAddress = serverAddress.replace(/:\/\/0.0.0.0/, "://localhost") + await open(openAddress).catch(console.error) + logger.info(` - Opened ${openAddress}`) + } +} + +const tryParse = (): Args => { + try { + return parse(process.argv.slice(2)) + } catch (error) { + console.error(error.message) + process.exit(1) + } +} + +const args = tryParse() +if (args.help) { + console.log("code-server", require("../../package.json").version) + console.log("") + console.log(`Usage: code-server [options] [path]`) + console.log("") + console.log("Options") + optionDescriptions().forEach((description) => { + console.log("", description) + }) +} else if (args.version) { + const version = require("../../package.json").version + if (args.json) { + console.log({ + codeServer: version, + vscode: require("../../lib/vscode/package.json").version, + }) + } else { + console.log(version) + } + process.exit(0) +} else if (args["list-extensions"] || args["install-extension"] || args["uninstall-extension"]) { + logger.debug("forking vs code cli...") + const vscode = cp.fork(path.resolve(__dirname, "../../lib/vscode/out/vs/server/fork"), [], { + env: { + ...process.env, + CODE_SERVER_PARENT_PID: process.pid.toString(), + }, + }) + vscode.once("message", (message) => { + logger.debug("Got message from VS Code", field("message", message)) + if (message.type !== "ready") { + logger.error("Unexpected response waiting for ready response") + process.exit(1) + } + const send: CliMessage = { type: "cli", args } + vscode.send(send) + }) + vscode.once("error", (error) => { + logger.error(error.message) + process.exit(1) + }) + vscode.on("exit", (code) => process.exit(code || 0)) +} else { + wrap(() => main(args)) +} diff --git a/src/node/http.ts b/src/node/http.ts new file mode 100644 index 000000000..adaa7a2ba --- /dev/null +++ b/src/node/http.ts @@ -0,0 +1,685 @@ +import { field, logger } from "@coder/logger" +import * as fs from "fs-extra" +import * as http from "http" +import * as httpolyglot from "httpolyglot" +import * as https from "https" +import * as net from "net" +import * as path from "path" +import * as querystring from "querystring" +import safeCompare from "safe-compare" +import { Readable } from "stream" +import * as tarFs from "tar-fs" +import * as tls from "tls" +import * as url from "url" +import * as zlib from "zlib" +import { HttpCode, HttpError } from "../common/http" +import { normalize, Options, plural, split } from "../common/util" +import { SocketProxyProvider } from "./socket" +import { getMediaMime, xdgLocalDir } from "./util" + +export type Cookies = { [key: string]: string[] | undefined } +export type PostData = { [key: string]: string | string[] | undefined } + +interface AuthPayload extends Cookies { + key?: string[] +} + +export enum AuthType { + Password = "password", + None = "none", +} + +export type Query = { [key: string]: string | string[] | undefined } + +export interface HttpResponse { + /* + * Whether to set cache-control headers for this response. + */ + cache?: boolean + /** + * If the code cannot be determined automatically set it here. The + * defaults are 302 for redirects and 200 for successful requests. For errors + * you should throw an HttpError and include the code there. If you + * use Error it will default to 404 for ENOENT and EISDIR and 500 otherwise. + */ + code?: number + /** + * Content to write in the response. Mutually exclusive with stream. + */ + content?: T + /** + * Cookie to write with the response. + * NOTE: Cookie paths must be absolute. The default is /. + */ + cookie?: { key: string; value: string; path?: string } + /** + * Used to automatically determine the appropriate mime type. + */ + filePath?: string + /** + * Additional headers to include. + */ + headers?: http.OutgoingHttpHeaders + /** + * If the mime type cannot be determined automatically set it here. + */ + mime?: string + /** + * Redirect to this path. Will rewrite against the base path but NOT the + * provider endpoint so you must include it. This allows redirecting outside + * of your endpoint. + */ + redirect?: string + /** + * Stream this to the response. Mutually exclusive with content. + */ + stream?: Readable + /** + * Query variables to add in addition to current ones when redirecting. Use + * `undefined` to remove a query variable. + */ + query?: Query +} + +/** + * Use when you need to run search and replace on a file's content before + * sending it. + */ +export interface HttpStringFileResponse extends HttpResponse { + content: string + filePath: string +} + +export interface RedirectResponse extends HttpResponse { + redirect: string +} + +export interface HttpServerOptions { + readonly auth?: AuthType + readonly cert?: string + readonly certKey?: string + readonly commit?: string + readonly host?: string + readonly password?: string + readonly port?: number + readonly socket?: string +} + +export interface Route { + base: string + requestPath: string + query: querystring.ParsedUrlQuery + fullPath: string + originalPath: string +} + +interface ProviderRoute extends Route { + provider: HttpProvider +} + +export interface HttpProviderOptions { + readonly auth: AuthType + readonly base: string + readonly commit: string + readonly password?: string +} + +/** + * Provides HTTP responses. This abstract class provides some helpers for + * interpreting, creating, and authenticating responses. + */ +export abstract class HttpProvider { + protected readonly rootPath = path.resolve(__dirname, "../..") + + public constructor(protected readonly options: HttpProviderOptions) {} + + public dispose(): void { + // No default behavior. + } + + /** + * Handle web sockets on the registered endpoint. + */ + public handleWebSocket( + /* eslint-disable @typescript-eslint/no-unused-vars */ + _route: Route, + _request: http.IncomingMessage, + _socket: net.Socket, + _head: Buffer, + /* eslint-enable @typescript-eslint/no-unused-vars */ + ): Promise { + throw new HttpError("Not found", HttpCode.NotFound) + } + + /** + * Handle requests to the registered endpoint. + */ + public abstract handleRequest(route: Route, request: http.IncomingMessage): Promise + + /** + * Get the base relative to the provided route. For each slash we need to go + * up a directory. For example: + * / => ./ + * /foo => ./ + * /foo/ => ./../ + * /foo/bar => ./../ + * /foo/bar/ => ./../../ + */ + public base(route: Route): string { + const depth = (route.originalPath.match(/\//g) || []).length + return normalize("./" + (depth > 1 ? "../".repeat(depth - 1) : "")) + } + + /** + * Get error response. + */ + public async getErrorRoot(route: Route, title: string, header: string, body: string): Promise { + const response = await this.getUtf8Resource(this.rootPath, "src/browser/pages/error.html") + response.content = response.content + .replace(/{{ERROR_TITLE}}/g, title) + .replace(/{{ERROR_HEADER}}/g, header) + .replace(/{{ERROR_BODY}}/g, body) + return this.replaceTemplates(route, response) + } + + /** + * Replace common templates strings. + */ + protected replaceTemplates( + route: Route, + response: HttpStringFileResponse, + sessionId?: string, + ): HttpStringFileResponse { + const options: Options = { + base: this.base(route), + commit: this.options.commit, + logLevel: logger.level, + sessionId, + } + response.content = response.content + .replace(/{{COMMIT}}/g, this.options.commit) + .replace(/{{TO}}/g, Array.isArray(route.query.to) ? route.query.to[0] : route.query.to || "/dashboard") + .replace(/{{BASE}}/g, this.base(route)) + .replace(/"{{OPTIONS}}"/, `'${JSON.stringify(options)}'`) + return response + } + + protected get isDev(): boolean { + return this.options.commit === "development" + } + + /** + * Get a file resource. + * TODO: Would a stream be faster, at least for large files? + */ + protected async getResource(...parts: string[]): Promise { + const filePath = path.join(...parts) + return { content: await fs.readFile(filePath), filePath } + } + + /** + * Get a file resource as a string. + */ + protected async getUtf8Resource(...parts: string[]): Promise { + const filePath = path.join(...parts) + return { content: await fs.readFile(filePath, "utf8"), filePath } + } + + /** + * Tar up and stream a directory. + */ + protected async getTarredResource(request: http.IncomingMessage, ...parts: string[]): Promise { + const filePath = path.join(...parts) + let stream: Readable = tarFs.pack(filePath) + const headers: http.OutgoingHttpHeaders = {} + if (request.headers["accept-encoding"] && request.headers["accept-encoding"].includes("gzip")) { + logger.debug("gzipping tar", field("filePath", filePath)) + const compress = zlib.createGzip() + stream.pipe(compress) + stream.on("error", (error) => compress.destroy(error)) + stream.on("close", () => compress.end()) + stream = compress + headers["content-encoding"] = "gzip" + } + return { stream, filePath, mime: "application/x-tar", cache: true, headers } + } + + /** + * Helper to error on invalid methods (default GET). + */ + protected ensureMethod(request: http.IncomingMessage, method?: string | string[]): void { + const check = Array.isArray(method) ? method : [method || "GET"] + if (!request.method || !check.includes(request.method)) { + throw new HttpError(`Unsupported method ${request.method}`, HttpCode.BadRequest) + } + } + + /** + * Helper to error if not authorized. + */ + protected ensureAuthenticated(request: http.IncomingMessage): void { + if (!this.authenticated(request)) { + throw new HttpError("Unauthorized", HttpCode.Unauthorized) + } + } + + /** + * Use the first query value or the default if there isn't one. + */ + protected queryOrDefault(value: string | string[] | undefined, def: string): string { + if (Array.isArray(value)) { + value = value[0] + } + return typeof value !== "undefined" ? value : def + } + + /** + * Return the provided password value if the payload contains the right + * password otherwise return false. If no payload is specified use cookies. + */ + protected authenticated(request: http.IncomingMessage, payload?: AuthPayload): string | boolean { + switch (this.options.auth) { + case AuthType.None: + return true + case AuthType.Password: + if (typeof payload === "undefined") { + payload = this.parseCookies(request) + } + if (this.options.password && payload.key) { + for (let i = 0; i < payload.key.length; ++i) { + if (safeCompare(payload.key[i], this.options.password)) { + return payload.key[i] + } + } + } + return false + default: + throw new Error(`Unsupported auth type ${this.options.auth}`) + } + } + + /** + * Parse POST data. + */ + protected getData(request: http.IncomingMessage): Promise { + return request.method === "POST" || request.method === "DELETE" + ? new Promise((resolve, reject) => { + let body = "" + const onEnd = (): void => { + off() // eslint-disable-line @typescript-eslint/no-use-before-define + resolve(body || undefined) + } + const onError = (error: Error): void => { + off() // eslint-disable-line @typescript-eslint/no-use-before-define + reject(error) + } + const onData = (d: Buffer): void => { + body += d + if (body.length > 1e6) { + onError(new HttpError("Payload is too large", HttpCode.LargePayload)) + request.connection.destroy() + } + } + const off = (): void => { + request.off("error", onError) + request.off("data", onError) + request.off("end", onEnd) + } + request.on("error", onError) + request.on("data", onData) + request.on("end", onEnd) + }) + : Promise.resolve(undefined) + } + + /** + * Parse cookies. + */ + protected parseCookies(request: http.IncomingMessage): T { + const cookies: { [key: string]: string[] } = {} + if (request.headers.cookie) { + request.headers.cookie.split(";").forEach((keyValue) => { + const [key, value] = split(keyValue, "=") + if (!cookies[key]) { + cookies[key] = [] + } + cookies[key].push(decodeURI(value)) + }) + } + return cookies as T + } +} + +/** + * Provides a heartbeat using a local file to indicate activity. + */ +export class Heart { + private heartbeatTimer?: NodeJS.Timeout + private heartbeatInterval = 60000 + private lastHeartbeat = 0 + + public constructor(private readonly heartbeatPath: string, private readonly isActive: () => Promise) {} + + /** + * Write to the heartbeat file if we haven't already done so within the + * timeout and start or reset a timer that keeps running as long as there is + * activity. Failures are logged as warnings. + */ + public beat(): void { + const now = Date.now() + if (now - this.lastHeartbeat >= this.heartbeatInterval) { + logger.trace("heartbeat") + fs.outputFile(this.heartbeatPath, "").catch((error) => { + logger.warn(error.message) + }) + this.lastHeartbeat = now + if (typeof this.heartbeatTimer !== "undefined") { + clearTimeout(this.heartbeatTimer) + } + this.heartbeatTimer = setTimeout(() => { + this.isActive() + .then((active) => { + if (active) { + this.beat() + } + }) + .catch((error) => { + logger.warn(error.message) + }) + }, this.heartbeatInterval) + } + } +} + +export interface HttpProvider0 { + new (options: HttpProviderOptions): T +} + +export interface HttpProvider1 { + new (options: HttpProviderOptions, a1: A1): T +} + +export interface HttpProvider2 { + new (options: HttpProviderOptions, a1: A1, a2: A2): T +} + +export interface HttpProvider3 { + new (options: HttpProviderOptions, a1: A1, a2: A2, a3: A3): T +} + +/** + * An HTTP server. Its main role is to route incoming HTTP requests to the + * appropriate provider for that endpoint then write out the response. It also + * covers some common use cases like redirects and caching. + */ +export class HttpServer { + protected readonly server: http.Server | https.Server + private listenPromise: Promise | undefined + public readonly protocol: "http" | "https" + private readonly providers = new Map() + private readonly heart: Heart + private readonly socketProvider = new SocketProxyProvider() + + public constructor(private readonly options: HttpServerOptions) { + this.heart = new Heart(path.join(xdgLocalDir, "heartbeat"), async () => { + const connections = await this.getConnections() + logger.trace(`${connections} active connection${plural(connections)}`) + return connections !== 0 + }) + this.protocol = this.options.cert ? "https" : "http" + if (this.protocol === "https") { + this.server = httpolyglot.createServer( + { + cert: this.options.cert && fs.readFileSync(this.options.cert), + key: this.options.certKey && fs.readFileSync(this.options.certKey), + }, + this.onRequest, + ) + } else { + this.server = http.createServer(this.onRequest) + } + } + + public dispose(): void { + this.socketProvider.stop() + this.providers.forEach((p) => p.dispose()) + } + + public async getConnections(): Promise { + return new Promise((resolve, reject) => { + this.server.getConnections((error, count) => { + return error ? reject(error) : resolve(count) + }) + }) + } + + /** + * Register a provider for a top-level endpoint. + */ + public registerHttpProvider(endpoint: string, provider: HttpProvider0): T + public registerHttpProvider(endpoint: string, provider: HttpProvider1, a1: A1): T + public registerHttpProvider( + endpoint: string, + provider: HttpProvider2, + a1: A1, + a2: A2, + ): T + public registerHttpProvider( + endpoint: string, + provider: HttpProvider3, + a1: A1, + a2: A2, + a3: A3, + ): T + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public registerHttpProvider(endpoint: string, provider: any, ...args: any[]): any { + endpoint = endpoint.replace(/^\/+|\/+$/g, "") + if (this.providers.has(`/${endpoint}`)) { + throw new Error(`${endpoint} is already registered`) + } + if (/\//.test(endpoint)) { + throw new Error(`Only top-level endpoints are supported (got ${endpoint})`) + } + const p = new provider( + { + auth: this.options.auth || AuthType.None, + base: `/${endpoint}`, + commit: this.options.commit, + password: this.options.password, + }, + ...args, + ) + this.providers.set(`/${endpoint}`, p) + return p + } + + /** + * Start listening on the specified port. + */ + public listen(): Promise { + if (!this.listenPromise) { + this.listenPromise = new Promise((resolve, reject) => { + this.server.on("error", reject) + this.server.on("upgrade", this.onUpgrade) + const onListen = (): void => resolve(this.address()) + if (this.options.socket) { + this.server.listen(this.options.socket, onListen) + } else { + this.server.listen(this.options.port, this.options.host, onListen) + } + }) + } + return this.listenPromise + } + + /** + * The *local* address of the server. + */ + public address(): string | null { + const address = this.server.address() + const endpoint = + typeof address !== "string" && address !== null + ? (address.address === "::" ? "localhost" : address.address) + ":" + address.port + : address + return endpoint && `${this.protocol}://${endpoint}` + } + + private onRequest = async (request: http.IncomingMessage, response: http.ServerResponse): Promise => { + this.heart.beat() + const route = this.parseUrl(request) + try { + const payload = this.maybeRedirect(request, route) || (await route.provider.handleRequest(route, request)) + if (!payload) { + throw new HttpError("Not found", HttpCode.NotFound) + } + response.writeHead(payload.redirect ? HttpCode.Redirect : payload.code || HttpCode.Ok, { + "Content-Type": payload.mime || getMediaMime(payload.filePath), + ...(payload.redirect ? { Location: this.constructRedirect(request, route, payload as RedirectResponse) } : {}), + ...(request.headers["service-worker"] ? { "Service-Worker-Allowed": route.provider.base(route) } : {}), + ...(payload.cache ? { "Cache-Control": "public, max-age=31536000" } : {}), + ...(payload.cookie + ? { + "Set-Cookie": [ + `${payload.cookie.key}=${payload.cookie.value}`, + `Path=${normalize(payload.cookie.path || "/", true)}`, + "HttpOnly", + "SameSite=strict", + ].join(";"), + } + : {}), + ...payload.headers, + }) + if (payload.stream) { + payload.stream.on("error", (error: NodeJS.ErrnoException) => { + response.writeHead(error.code === "ENOENT" ? HttpCode.NotFound : HttpCode.ServerError) + response.end(error.message) + }) + payload.stream.on("close", () => response.end()) + payload.stream.pipe(response) + } else if (typeof payload.content === "string" || payload.content instanceof Buffer) { + response.end(payload.content) + } else if (payload.content && typeof payload.content === "object") { + response.end(JSON.stringify(payload.content)) + } else { + response.end() + } + } catch (error) { + let e = error + if (error.code === "ENOENT" || error.code === "EISDIR") { + e = new HttpError("Not found", HttpCode.NotFound) + } + logger.debug("Request error", field("url", request.url)) + logger.debug(error.stack) + const code = typeof e.code === "number" ? e.code : HttpCode.ServerError + const content = (await route.provider.getErrorRoot(route, code, code, e.message)).content + response.writeHead(code) + response.end(content) + } + } + + /** + * Return any necessary redirection before delegating to a provider. + */ + private maybeRedirect(request: http.IncomingMessage, route: ProviderRoute): RedirectResponse | undefined { + // If we're handling TLS ensure all requests are redirected to HTTPS. + if (this.options.cert && !(request.connection as tls.TLSSocket).encrypted) { + return { redirect: route.fullPath } + } + + return undefined + } + + /** + * Given a path that goes from the base, construct a relative redirect URL + * that will get you there considering that the app may be served from an + * unknown base path. If handling TLS, also ensure HTTPS. + */ + private constructRedirect(request: http.IncomingMessage, route: ProviderRoute, payload: RedirectResponse): string { + const query = { + ...route.query, + ...(payload.query || {}), + } + + Object.keys(query).forEach((key) => { + if (typeof query[key] === "undefined") { + delete query[key] + } + }) + + const secure = (request.connection as tls.TLSSocket).encrypted + const redirect = + (this.options.cert && !secure ? `${this.protocol}://${request.headers.host}/` : "") + + normalize(`${route.provider.base(route)}/${payload.redirect}`, true) + + (Object.keys(query).length > 0 ? `?${querystring.stringify(query)}` : "") + logger.debug("Redirecting", field("secure", !!secure), field("from", request.url), field("to", redirect)) + return redirect + } + + private onUpgrade = async (request: http.IncomingMessage, socket: net.Socket, head: Buffer): Promise => { + try { + this.heart.beat() + socket.on("error", () => socket.destroy()) + + if (this.options.cert && !(socket as tls.TLSSocket).encrypted) { + throw new HttpError("HTTP websocket", HttpCode.BadRequest) + } + + if (!request.headers.upgrade || request.headers.upgrade.toLowerCase() !== "websocket") { + throw new HttpError("HTTP/1.1 400 Bad Request", HttpCode.BadRequest) + } + + const route = this.parseUrl(request) + if (!route.provider) { + throw new HttpError("Not found", HttpCode.NotFound) + } + + if ( + !(await route.provider.handleWebSocket(route, request, await this.socketProvider.createProxy(socket), head)) + ) { + throw new HttpError("Not found", HttpCode.NotFound) + } + } catch (error) { + socket.destroy(error) + logger.warn(`discarding socket connection: ${error.message}`) + } + } + + /** + * Parse a request URL so we can route it. + */ + private parseUrl(request: http.IncomingMessage): ProviderRoute { + const parse = (fullPath: string): { base: string; requestPath: string } => { + const match = fullPath.match(/^(\/?[^/]*)(.*)$/) + let [, /* ignore */ base, requestPath] = match ? match.map((p) => p.replace(/\/+$/, "")) : ["", "", ""] + if (base.indexOf(".") !== -1) { + // Assume it's a file at the root. + requestPath = base + base = "/" + } else if (base === "") { + // Happens if it's a plain `domain.com`. + base = "/" + } + requestPath = requestPath || "/index.html" + return { base, requestPath } + } + + const parsedUrl = request.url ? url.parse(request.url, true) : { query: {}, pathname: "" } + const originalPath = parsedUrl.pathname || "/" + const fullPath = normalize(originalPath, true) + const { base, requestPath } = parse(fullPath) + + // Providers match on the path after their base so we need to account for + // that by shifting the next base out of the request path. + let provider = this.providers.get(base) + if (base !== "/" && provider) { + return { ...parse(requestPath), fullPath, query: parsedUrl.query, provider, originalPath } + } + + // Fall back to the top-level provider. + provider = this.providers.get("/") + if (!provider) { + throw new Error(`No provider for ${base}`) + } + return { base, fullPath, requestPath, query: parsedUrl.query, provider, originalPath } + } +} diff --git a/src/node/insights.ts b/src/node/insights.ts deleted file mode 100644 index 691d9e3ea..000000000 --- a/src/node/insights.ts +++ /dev/null @@ -1,124 +0,0 @@ -import * as appInsights from "applicationinsights"; -import * as https from "https"; -import * as http from "http"; -import * as os from "os"; - -class Channel { - public get _sender() { - throw new Error("unimplemented"); - } - public get _buffer() { - throw new Error("unimplemented"); - } - - public setUseDiskRetryCaching(): void { - throw new Error("unimplemented"); - } - public send(): void { - throw new Error("unimplemented"); - } - public triggerSend(): void { - throw new Error("unimplemented"); - } -} - -export class TelemetryClient { - public context: any = undefined; - public commonProperties: any = undefined; - public config: any = {}; - - public channel: any = new Channel(); - - public addTelemetryProcessor(): void { - throw new Error("unimplemented"); - } - - public clearTelemetryProcessors(): void { - throw new Error("unimplemented"); - } - - public runTelemetryProcessors(): void { - throw new Error("unimplemented"); - } - - public trackTrace(): void { - throw new Error("unimplemented"); - } - - public trackMetric(): void { - throw new Error("unimplemented"); - } - - public trackException(): void { - throw new Error("unimplemented"); - } - - public trackRequest(): void { - throw new Error("unimplemented"); - } - - public trackDependency(): void { - throw new Error("unimplemented"); - } - - public track(): void { - throw new Error("unimplemented"); - } - - public trackNodeHttpRequestSync(): void { - throw new Error("unimplemented"); - } - - public trackNodeHttpRequest(): void { - throw new Error("unimplemented"); - } - - public trackNodeHttpDependency(): void { - throw new Error("unimplemented"); - } - - public trackEvent(options: appInsights.Contracts.EventTelemetry): void { - if (!options.properties) { - options.properties = {}; - } - if (!options.measurements) { - options.measurements = {}; - } - - try { - const cpus = os.cpus(); - options.measurements.cores = cpus.length; - options.properties["common.cpuModel"] = cpus[0].model; - } catch (error) {} - - try { - options.measurements.memoryFree = os.freemem(); - options.measurements.memoryTotal = os.totalmem(); - } catch (error) {} - - try { - options.properties["common.shell"] = os.userInfo().shell; - options.properties["common.release"] = os.release(); - options.properties["common.arch"] = os.arch(); - } catch (error) {} - - try { - const url = process.env.TELEMETRY_URL || "https://v1.telemetry.coder.com/track"; - const request = (/^http:/.test(url) ? http : https).request(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - }); - request.on("error", () => { /* We don"t care. */ }); - request.write(JSON.stringify(options)); - request.end(); - } catch (error) {} - } - - public flush(options: { callback: (v: string) => void }): void { - if (options.callback) { - options.callback(""); - } - } -} diff --git a/src/node/ipc.ts b/src/node/ipc.ts deleted file mode 100644 index c92ea8726..000000000 --- a/src/node/ipc.ts +++ /dev/null @@ -1,61 +0,0 @@ -import * as cp from "child_process"; -import { Emitter } from "vs/base/common/event"; - -enum ControlMessage { - okToChild = "ok>", - okFromChild = "ok<", -} - -interface RelaunchMessage { - type: "relaunch"; - version: string; -} - -export type Message = RelaunchMessage; - -class IpcMain { - protected readonly _onMessage = new Emitter(); - public readonly onMessage = this._onMessage.event; - - public handshake(child?: cp.ChildProcess): Promise { - return new Promise((resolve, reject) => { - const target = child || process; - if (!target.send) { - throw new Error("Not spawned with IPC enabled"); - } - target.on("message", (message) => { - if (message === child ? ControlMessage.okFromChild : ControlMessage.okToChild) { - target.removeAllListeners(); - target.on("message", (msg) => this._onMessage.fire(msg)); - if (child) { - target.send!(ControlMessage.okToChild); - } - resolve(); - } - }); - if (child) { - child.once("error", reject); - child.once("exit", (code) => { - const error = new Error(`Unexpected exit with code ${code}`); - (error as any).code = code; - reject(error); - }); - } else { - target.send(ControlMessage.okFromChild); - } - }); - } - - public relaunch(version: string): void { - this.send({ type: "relaunch", version }); - } - - private send(message: Message): void { - if (!process.send) { - throw new Error("Not a child process with IPC enabled"); - } - process.send(message); - } -} - -export const ipcMain = new IpcMain(); diff --git a/src/node/marketplace.ts b/src/node/marketplace.ts deleted file mode 100644 index 9aec797c0..000000000 --- a/src/node/marketplace.ts +++ /dev/null @@ -1,176 +0,0 @@ -import * as fs from "fs"; -import * as path from "path"; -import * as util from "util"; -import { CancellationToken } from "vs/base/common/cancellation"; -import { mkdirp } from "vs/base/node/pfs"; -import * as vszip from "vs/base/node/zip"; -import * as nls from "vs/nls"; -import product from "vs/platform/product/common/product"; -import { localRequire } from "vs/server/src/node/util"; - -const tarStream = localRequire("tar-stream/index"); - -// We will be overriding these, so keep a reference to the original. -const vszipExtract = vszip.extract; -const vszipBuffer = vszip.buffer; - -export interface IExtractOptions { - overwrite?: boolean; - /** - * Source path within the TAR/ZIP archive. Only the files - * contained in this path will be extracted. - */ - sourcePath?: string; -} - -export interface IFile { - path: string; - contents?: Buffer | string; - localPath?: string; -} - -export const tar = async (tarPath: string, files: IFile[]): Promise => { - const pack = tarStream.pack(); - const chunks: Buffer[] = []; - const ended = new Promise((resolve) => { - pack.on("end", () => resolve(Buffer.concat(chunks))); - }); - pack.on("data", (chunk: Buffer) => chunks.push(chunk)); - for (let i = 0; i < files.length; i++) { - const file = files[i]; - pack.entry({ name: file.path }, file.contents); - } - pack.finalize(); - await util.promisify(fs.writeFile)(tarPath, await ended); - return tarPath; -}; - -export const extract = async (archivePath: string, extractPath: string, options: IExtractOptions = {}, token: CancellationToken): Promise => { - try { - await extractTar(archivePath, extractPath, options, token); - } catch (error) { - if (error.toString().includes("Invalid tar header")) { - await vszipExtract(archivePath, extractPath, options, token); - } - } -}; - -export const buffer = (targetPath: string, filePath: string): Promise => { - return new Promise(async (resolve, reject) => { - try { - let done: boolean = false; - await extractAssets(targetPath, new RegExp(filePath), (assetPath: string, data: Buffer) => { - if (path.normalize(assetPath) === path.normalize(filePath)) { - done = true; - resolve(data); - } - }); - if (!done) { - throw new Error("couldn't find asset " + filePath); - } - } catch (error) { - if (error.toString().includes("Invalid tar header")) { - vszipBuffer(targetPath, filePath).then(resolve).catch(reject); - } else { - reject(error); - } - } - }); -}; - -const extractAssets = async (tarPath: string, match: RegExp, callback: (path: string, data: Buffer) => void): Promise => { - return new Promise((resolve, reject): void => { - const extractor = tarStream.extract(); - const fail = (error: Error) => { - extractor.destroy(); - reject(error); - }; - extractor.once("error", fail); - extractor.on("entry", async (header, stream, next) => { - const name = header.name; - if (match.test(name)) { - extractData(stream).then((data) => { - callback(name, data); - next(); - }).catch(fail); - } else { - stream.on("end", () => next()); - stream.resume(); // Just drain it. - } - }); - extractor.on("finish", resolve); - fs.createReadStream(tarPath).pipe(extractor); - }); -}; - -const extractData = (stream: NodeJS.ReadableStream): Promise => { - return new Promise((resolve, reject): void => { - const fileData: Buffer[] = []; - stream.on("error", reject); - stream.on("end", () => resolve(Buffer.concat(fileData))); - stream.on("data", (data) => fileData.push(data)); - }); -}; - -const extractTar = async (tarPath: string, targetPath: string, options: IExtractOptions = {}, token: CancellationToken): Promise => { - return new Promise((resolve, reject): void => { - const sourcePathRegex = new RegExp(options.sourcePath ? `^${options.sourcePath}` : ""); - const extractor = tarStream.extract(); - const fail = (error: Error) => { - extractor.destroy(); - reject(error); - }; - extractor.once("error", fail); - extractor.on("entry", async (header, stream, next) => { - const nextEntry = (): void => { - stream.on("end", () => next()); - stream.resume(); - }; - - const rawName = path.normalize(header.name); - if (token.isCancellationRequested || !sourcePathRegex.test(rawName)) { - return nextEntry(); - } - - const fileName = rawName.replace(sourcePathRegex, ""); - const targetFileName = path.join(targetPath, fileName); - if (/\/$/.test(fileName)) { - return mkdirp(targetFileName).then(nextEntry); - } - - const dirName = path.dirname(fileName); - const targetDirName = path.join(targetPath, dirName); - if (targetDirName.indexOf(targetPath) !== 0) { - return fail(new Error(nls.localize("invalid file", "Error extracting {0}. Invalid file.", fileName))); - } - - await mkdirp(targetDirName, undefined); - - const fstream = fs.createWriteStream(targetFileName, { mode: header.mode }); - fstream.once("close", () => next()); - fstream.once("error", fail); - stream.pipe(fstream); - }); - extractor.once("finish", resolve); - fs.createReadStream(tarPath).pipe(extractor); - }); -}; - -/** - * Override original functionality so we can use a custom marketplace with - * either tars or zips. - */ -export const enableCustomMarketplace = (): void => { - (product).extensionsGallery = { // Use `any` to override readonly. - serviceUrl: process.env.SERVICE_URL || "https://v1.extapi.coder.com", - itemUrl: process.env.ITEM_URL || "", - controlUrl: "", - recommendationsUrl: "", - ...(product.extensionsGallery || {}), - }; - - const target = vszip as typeof vszip; - target.zip = tar; - target.extract = extract; - target.buffer = buffer; -}; diff --git a/src/node/nls.ts b/src/node/nls.ts deleted file mode 100644 index 6f8d4e3d8..000000000 --- a/src/node/nls.ts +++ /dev/null @@ -1,86 +0,0 @@ -import * as fs from "fs"; -import * as path from "path"; -import * as util from "util"; -import { getPathFromAmdModule } from "vs/base/common/amd"; -import * as lp from "vs/base/node/languagePacks"; -import product from "vs/platform/product/common/product"; -import { Translations } from "vs/workbench/services/extensions/common/extensionPoints"; - -const configurations = new Map>(); -const metadataPath = path.join(getPathFromAmdModule(require, ""), "nls.metadata.json"); - -export const isInternalConfiguration = (config: lp.NLSConfiguration): config is lp.InternalNLSConfiguration => { - return config && !!(config)._languagePackId; -}; - -const DefaultConfiguration = { - locale: "en", - availableLanguages: {}, -}; - -export const getNlsConfiguration = async (locale: string, userDataPath: string): Promise => { - const id = `${locale}: ${userDataPath}`; - if (!configurations.has(id)) { - configurations.set(id, new Promise(async (resolve) => { - const config = product.commit && await util.promisify(fs.exists)(metadataPath) - ? await lp.getNLSConfiguration(product.commit, userDataPath, metadataPath, locale) - : DefaultConfiguration; - if (isInternalConfiguration(config)) { - config._languagePackSupport = true; - } - // If the configuration has no results keep trying since code-server - // doesn't restart when a language is installed so this result would - // persist (the plugin might not be installed yet or something). - if (config.locale !== "en" && config.locale !== "en-us" && Object.keys(config.availableLanguages).length === 0) { - configurations.delete(id); - } - resolve(config); - })); - } - return configurations.get(id)!; -}; - -export const getTranslations = async (locale: string, userDataPath: string): Promise => { - const config = await getNlsConfiguration(locale, userDataPath); - if (isInternalConfiguration(config)) { - try { - return JSON.parse(await util.promisify(fs.readFile)(config._translationsConfigFile, "utf8")); - } catch (error) { /* Nothing yet. */} - } - return {}; -}; - -export const getLocaleFromConfig = async (userDataPath: string): Promise => { - let locale = "en"; - try { - const localeConfigUri = path.join(userDataPath, "User/locale.json"); - const content = stripComments(await util.promisify(fs.readFile)(localeConfigUri, "utf8")); - locale = JSON.parse(content).locale; - } catch (error) { /* Ignore. */ } - return locale; -}; - -// Taken from src/main.js in the main VS Code source. -const stripComments = (content: string): string => { - const regexp = /("(?:[^\\"]*(?:\\.)?)*")|('(?:[^\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g; - - return content.replace(regexp, (match, _m1, _m2, m3, m4) => { - // Only one of m1, m2, m3, m4 matches - if (m3) { - // A block comment. Replace with nothing - return ''; - } else if (m4) { - // A line comment. If it ends in \r?\n then keep it. - const length_1 = m4.length; - if (length_1 > 2 && m4[length_1 - 1] === '\n') { - return m4[length_1 - 2] === '\r' ? '\r\n' : '\n'; - } - else { - return ''; - } - } else { - // We match a string - return match; - } - }); -}; diff --git a/src/node/protocol.ts b/src/node/protocol.ts deleted file mode 100644 index fde6662a6..000000000 --- a/src/node/protocol.ts +++ /dev/null @@ -1,73 +0,0 @@ -import * as net from "net"; -import { VSBuffer } from "vs/base/common/buffer"; -import { PersistentProtocol } from "vs/base/parts/ipc/common/ipc.net"; -import { NodeSocket, WebSocketNodeSocket } from "vs/base/parts/ipc/node/ipc.net"; -import { AuthRequest, ConnectionTypeRequest, HandshakeMessage } from "vs/platform/remote/common/remoteAgentConnection"; - -export interface SocketOptions { - readonly reconnectionToken: string; - readonly reconnection: boolean; - readonly skipWebSocketFrames: boolean; -} - -export class Protocol extends PersistentProtocol { - public constructor(socket: net.Socket, public readonly options: SocketOptions) { - super( - options.skipWebSocketFrames - ? new NodeSocket(socket) - : new WebSocketNodeSocket(new NodeSocket(socket)), - ); - } - - public getUnderlyingSocket(): net.Socket { - const socket = this.getSocket(); - return socket instanceof NodeSocket - ? socket.socket - : (socket as WebSocketNodeSocket).socket.socket; - } - - /** - * Perform a handshake to get a connection request. - */ - public handshake(): Promise { - return new Promise((resolve, reject) => { - const handler = this.onControlMessage((rawMessage) => { - try { - const message = JSON.parse(rawMessage.toString()); - switch (message.type) { - case "auth": return this.authenticate(message); - case "connectionType": - handler.dispose(); - return resolve(message); - default: throw new Error("Unrecognized message type"); - } - } catch (error) { - handler.dispose(); - reject(error); - } - }); - }); - } - - /** - * TODO: This ignores the authentication process entirely for now. - */ - private authenticate(_message: AuthRequest): void { - this.sendMessage({ type: "sign", data: "" }); - } - - /** - * TODO: implement. - */ - public tunnel(): void { - throw new Error("Tunnel is not implemented yet"); - } - - /** - * Send a handshake message. In the case of the extension host, it just sends - * back a debug port. - */ - public sendMessage(message: HandshakeMessage | { debugPort?: number } ): void { - this.sendControl(VSBuffer.fromString(JSON.stringify(message))); - } -} diff --git a/src/node/server.ts b/src/node/server.ts deleted file mode 100644 index 5db95f7a2..000000000 --- a/src/node/server.ts +++ /dev/null @@ -1,966 +0,0 @@ -import * as crypto from "crypto"; -import * as fs from "fs"; -import * as http from "http"; -import * as https from "https"; -import * as net from "net"; -import * as path from "path"; -import * as querystring from "querystring"; -import { Readable } from "stream"; -import * as tls from "tls"; -import * as url from "url"; -import * as util from "util"; -import { Emitter } from "vs/base/common/event"; -import { Schemas } from "vs/base/common/network"; -import { URI, UriComponents } from "vs/base/common/uri"; -import { generateUuid } from "vs/base/common/uuid"; -import { getMachineId } from 'vs/base/node/id'; -import { NLSConfiguration } from "vs/base/node/languagePacks"; -import { mkdirp, rimraf } from "vs/base/node/pfs"; -import { ClientConnectionEvent, IPCServer, IServerChannel } from "vs/base/parts/ipc/common/ipc"; -import { createChannelReceiver } from "vs/base/parts/ipc/node/ipc"; -import { LogsDataCleaner } from "vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner"; -import { IConfigurationService } from "vs/platform/configuration/common/configuration"; -import { ConfigurationService } from "vs/platform/configuration/node/configurationService"; -import { ExtensionHostDebugBroadcastChannel } from "vs/platform/debug/common/extensionHostDebugIpc"; -import { IEnvironmentService, ParsedArgs } from "vs/platform/environment/common/environment"; -import { EnvironmentService } from "vs/platform/environment/node/environmentService"; -import { ExtensionGalleryService } from "vs/platform/extensionManagement/common/extensionGalleryService"; -import { IExtensionGalleryService, IExtensionManagementService } from "vs/platform/extensionManagement/common/extensionManagement"; -import { ExtensionManagementChannel } from "vs/platform/extensionManagement/common/extensionManagementIpc"; -import { ExtensionManagementService } from "vs/platform/extensionManagement/node/extensionManagementService"; -import { IFileService } from "vs/platform/files/common/files"; -import { FileService } from "vs/platform/files/common/fileService"; -import { DiskFileSystemProvider } from "vs/platform/files/node/diskFileSystemProvider"; -import { SyncDescriptor } from "vs/platform/instantiation/common/descriptors"; -import { InstantiationService } from "vs/platform/instantiation/common/instantiationService"; -import { ServiceCollection } from "vs/platform/instantiation/common/serviceCollection"; -import { ILocalizationsService } from "vs/platform/localizations/common/localizations"; -import { LocalizationsService } from "vs/platform/localizations/node/localizations"; -import { getLogLevel, ILogService } from "vs/platform/log/common/log"; -import { LoggerChannel } from "vs/platform/log/common/logIpc"; -import { SpdLogService } from "vs/platform/log/node/spdlogService"; -import product from 'vs/platform/product/common/product'; -import { IProductService } from "vs/platform/product/common/productService"; -import { ConnectionType, ConnectionTypeRequest } from "vs/platform/remote/common/remoteAgentConnection"; -import { RemoteAgentConnectionContext } from "vs/platform/remote/common/remoteAgentEnvironment"; -import { REMOTE_FILE_SYSTEM_CHANNEL_NAME } from "vs/workbench/services/remote/common/remoteAgentFileSystemChannel"; -import { IRequestService } from "vs/platform/request/common/request"; -import { RequestChannel } from "vs/platform/request/common/requestIpc"; -import { RequestService } from "vs/platform/request/node/requestService"; -import ErrorTelemetry from "vs/platform/telemetry/browser/errorTelemetry"; -import { ITelemetryService } from "vs/platform/telemetry/common/telemetry"; -import { ITelemetryServiceConfig, TelemetryService } from "vs/platform/telemetry/common/telemetryService"; -import { combinedAppender, LogAppender, NullTelemetryService } from "vs/platform/telemetry/common/telemetryUtils"; -import { AppInsightsAppender } from "vs/platform/telemetry/node/appInsightsAppender"; -import { resolveCommonProperties } from "vs/platform/telemetry/node/commonProperties"; -import { UpdateChannel } from "vs/platform/update/electron-main/updateIpc"; -import { INodeProxyService, NodeProxyChannel } from "vs/server/src/common/nodeProxy"; -import { TelemetryChannel } from "vs/server/src/common/telemetry"; -import { split } from "vs/server/src/common/util"; -import { ExtensionEnvironmentChannel, FileProviderChannel, NodeProxyService } from "vs/server/src/node/channel"; -import { Connection, ExtensionHostConnection, ManagementConnection } from "vs/server/src/node/connection"; -import { TelemetryClient } from "vs/server/src/node/insights"; -import { getLocaleFromConfig, getNlsConfiguration } from "vs/server/src/node/nls"; -import { Protocol } from "vs/server/src/node/protocol"; -import { UpdateService } from "vs/server/src/node/update"; -import { AuthType, getMediaMime, getUriTransformer, hash, localRequire, tmpdir } from "vs/server/src/node/util"; -import { RemoteExtensionLogFileName } from "vs/workbench/services/remote/common/remoteAgentService"; -import { IWorkbenchConstructionOptions } from "vs/workbench/workbench.web.api"; - -const tarFs = localRequire("tar-fs/index"); - -export enum HttpCode { - Ok = 200, - Redirect = 302, - NotFound = 404, - BadRequest = 400, - Unauthorized = 401, - LargePayload = 413, - ServerError = 500, -} - -export interface Options { - WORKBENCH_WEB_CONFIGURATION: IWorkbenchConstructionOptions & { folderUri?: UriComponents, workspaceUri?: UriComponents }; - REMOTE_USER_DATA_URI: UriComponents | URI; - PRODUCT_CONFIGURATION: Partial; - NLS_CONFIGURATION: NLSConfiguration; -} - -export interface Response { - cache?: boolean; - code?: number; - content?: string | Buffer; - filePath?: string; - headers?: http.OutgoingHttpHeaders; - mime?: string; - redirect?: string; - stream?: Readable; -} - -export interface LoginPayload { - password?: string; -} - -export interface AuthPayload { - key?: string[]; -} - -export class HttpError extends Error { - public constructor(message: string, public readonly code: number) { - super(message); - // @ts-ignore - this.name = this.constructor.name; - Error.captureStackTrace(this, this.constructor); - } -} - -export interface ServerOptions { - readonly auth: AuthType; - readonly basePath?: string; - readonly connectionToken?: string; - readonly cert?: string; - readonly certKey?: string; - readonly openUri?: string; - readonly host?: string; - readonly password?: string; - readonly port?: number; - readonly socket?: string; -} - -export abstract class Server { - protected readonly server: http.Server | https.Server; - protected rootPath = path.resolve(__dirname, "../../../../.."); - protected serverRoot = path.join(this.rootPath, "/out/vs/server/src"); - protected readonly allowedRequestPaths: string[] = [this.rootPath]; - private listenPromise: Promise | undefined; - public readonly protocol: "http" | "https"; - public readonly options: ServerOptions; - - public constructor(options: ServerOptions) { - this.options = { - host: options.auth === "password" && options.cert ? "0.0.0.0" : "localhost", - ...options, - basePath: options.basePath ? options.basePath.replace(/\/+$/, "") : "", - password: options.password ? hash(options.password) : undefined, - }; - this.protocol = this.options.cert ? "https" : "http"; - if (this.protocol === "https") { - const httpolyglot = localRequire("httpolyglot/lib/index"); - this.server = httpolyglot.createServer({ - cert: this.options.cert && fs.readFileSync(this.options.cert), - key: this.options.certKey && fs.readFileSync(this.options.certKey), - }, this.onRequest); - } else { - this.server = http.createServer(this.onRequest); - } - } - - public listen(): Promise { - if (!this.listenPromise) { - this.listenPromise = new Promise((resolve, reject) => { - this.server.on("error", reject); - this.server.on("upgrade", this.onUpgrade); - const onListen = () => resolve(this.address()); - if (this.options.socket) { - this.server.listen(this.options.socket, onListen); - } else { - this.server.listen(this.options.port, this.options.host, onListen); - } - }); - } - return this.listenPromise; - } - - /** - * The *local* address of the server. - */ - public address(): string { - const address = this.server.address(); - const endpoint = address && typeof address !== "string" - ? (address.address === "::" ? "localhost" : address.address) + ":" + address.port - : address; - return `${this.protocol}://${endpoint}`; - } - - protected abstract handleWebSocket( - socket: net.Socket, - parsedUrl: url.UrlWithParsedQuery - ): Promise; - - protected abstract handleRequest( - base: string, - requestPath: string, - parsedUrl: url.UrlWithParsedQuery, - request: http.IncomingMessage, - ): Promise; - - protected async getResource(...parts: string[]): Promise { - const filePath = this.ensureAuthorizedFilePath(...parts); - return { content: await util.promisify(fs.readFile)(filePath), filePath }; - } - - protected async getAnyResource(...parts: string[]): Promise { - const filePath = path.join(...parts); - return { content: await util.promisify(fs.readFile)(filePath), filePath }; - } - - protected async getTarredResource(...parts: string[]): Promise { - const filePath = this.ensureAuthorizedFilePath(...parts); - return { stream: tarFs.pack(filePath), filePath, mime: "application/tar", cache: true }; - } - - protected ensureAuthorizedFilePath(...parts: string[]): string { - const filePath = path.join(...parts); - if (!this.isAllowedRequestPath(filePath)) { - throw new HttpError("Unauthorized", HttpCode.Unauthorized); - } - return filePath; - } - - protected withBase(request: http.IncomingMessage, path: string): string { - const [, query] = request.url ? split(request.url, "?") : []; - return `${this.protocol}://${request.headers.host}${this.options.basePath}${path}${query ? `?${query}` : ""}`; - } - - private isAllowedRequestPath(path: string): boolean { - for (let i = 0; i < this.allowedRequestPaths.length; ++i) { - if (path.indexOf(this.allowedRequestPaths[i]) === 0) { - return true; - } - } - return false; - } - - private onRequest = async (request: http.IncomingMessage, response: http.ServerResponse): Promise => { - try { - const parsedUrl = url.parse(request.url || "", true); - const payload = await this.preHandleRequest(request, parsedUrl); - response.writeHead(payload.redirect ? HttpCode.Redirect : payload.code || HttpCode.Ok, { - "Content-Type": payload.mime || getMediaMime(payload.filePath), - ...(payload.redirect ? { Location: this.withBase(request, payload.redirect) } : {}), - ...(request.headers["service-worker"] ? { "Service-Worker-Allowed": this.options.basePath || "/" } : {}), - ...(payload.cache ? { "Cache-Control": "public, max-age=31536000" } : {}), - ...payload.headers, - }); - if (payload.stream) { - payload.stream.on("error", (error: NodeJS.ErrnoException) => { - response.writeHead(error.code === "ENOENT" ? HttpCode.NotFound : HttpCode.ServerError); - response.end(error.message); - }); - payload.stream.pipe(response); - } else { - response.end(payload.content); - } - } catch (error) { - if (error.code === "ENOENT" || error.code === "EISDIR") { - error = new HttpError("Not found", HttpCode.NotFound); - } - response.writeHead(typeof error.code === "number" ? error.code : HttpCode.ServerError); - response.end(error.message); - } - }; - - private async preHandleRequest(request: http.IncomingMessage, parsedUrl: url.UrlWithParsedQuery): Promise { - const secure = (request.connection as tls.TLSSocket).encrypted; - if (this.options.cert && !secure) { - return { redirect: request.url }; - } - - const fullPath = decodeURIComponent(parsedUrl.pathname || "/"); - const match = fullPath.match(/^(\/?[^/]*)(.*)$/); - let [/* ignore */, base, requestPath] = match - ? match.map((p) => p.replace(/\/+$/, "")) - : ["", "", ""]; - if (base.indexOf(".") !== -1) { // Assume it's a file at the root. - requestPath = base; - base = "/"; - } else if (base === "") { // Happens if it's a plain `domain.com`. - base = "/"; - } - base = path.normalize(base); - requestPath = path.normalize(requestPath || "/index.html"); - - if (base !== "/login" || this.options.auth !== "password" || requestPath !== "/index.html") { - this.ensureGet(request); - } - - // Allow for a versioned static endpoint. This lets us cache every static - // resource underneath the path based on the version without any work and - // without adding query parameters which have their own issues. - // REVIEW: Discuss whether this is the best option; this is sort of a quick - // hack almost to get caching in the meantime but it does work pretty well. - if (/^\/static-/.test(base)) { - base = "/static"; - } - - switch (base) { - case "/": - switch (requestPath) { - // NOTE: This must be served at the correct location based on the - // start_url in the manifest. - case "/manifest.json": - case "/code-server.png": - const response = await this.getResource(this.serverRoot, "media", requestPath); - response.cache = true; - return response; - } - if (!this.authenticate(request)) { - return { redirect: "/login" }; - } - break; - case "/static": - const response = await this.getResource(this.rootPath, requestPath); - response.cache = true; - return response; - case "/login": - if (this.options.auth !== "password" || requestPath !== "/index.html") { - throw new HttpError("Not found", HttpCode.NotFound); - } - return this.tryLogin(request); - default: - if (!this.authenticate(request)) { - throw new HttpError("Unauthorized", HttpCode.Unauthorized); - } - break; - } - - return this.handleRequest(base, requestPath, parsedUrl, request); - } - - private onUpgrade = async (request: http.IncomingMessage, socket: net.Socket): Promise => { - try { - await this.preHandleWebSocket(request, socket); - } catch (error) { - socket.destroy(); - console.error(error.message); - } - }; - - private preHandleWebSocket(request: http.IncomingMessage, socket: net.Socket): Promise { - socket.on("error", () => socket.destroy()); - socket.on("end", () => socket.destroy()); - - this.ensureGet(request); - if (!this.authenticate(request)) { - throw new HttpError("Unauthorized", HttpCode.Unauthorized); - } else if (!request.headers.upgrade || request.headers.upgrade.toLowerCase() !== "websocket") { - throw new Error("HTTP/1.1 400 Bad Request"); - } - - // This magic value is specified by the websocket spec. - const magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - const reply = crypto.createHash("sha1") - .update(request.headers["sec-websocket-key"] + magic) - .digest("base64"); - socket.write([ - "HTTP/1.1 101 Switching Protocols", - "Upgrade: websocket", - "Connection: Upgrade", - `Sec-WebSocket-Accept: ${reply}`, - ].join("\r\n") + "\r\n\r\n"); - - const parsedUrl = url.parse(request.url || "", true); - return this.handleWebSocket(socket, parsedUrl); - } - - private async tryLogin(request: http.IncomingMessage): Promise { - const redirect = (password: string | true) => { - return { - redirect: "/", - headers: typeof password === "string" - ? { "Set-Cookie": `key=${password}; Path=${this.options.basePath || "/"}; HttpOnly; SameSite=strict` } - : {}, - }; - }; - const providedPassword = this.authenticate(request); - if (providedPassword && (request.method === "GET" || request.method === "POST")) { - return redirect(providedPassword); - } - if (request.method === "POST") { - const data = await this.getData(request); - const password = this.authenticate(request, { - key: typeof data.password === "string" ? [hash(data.password)] : undefined, - }); - if (password) { - return redirect(password); - } - console.error("Failed login attempt", JSON.stringify({ - xForwardedFor: request.headers["x-forwarded-for"], - remoteAddress: request.connection.remoteAddress, - userAgent: request.headers["user-agent"], - timestamp: Math.floor(new Date().getTime() / 1000), - })); - return this.getLogin("Invalid password", data); - } - this.ensureGet(request); - return this.getLogin(); - } - - private async getLogin(error: string = "", payload?: LoginPayload): Promise { - const filePath = path.join(this.serverRoot, "browser/login.html"); - const content = (await util.promisify(fs.readFile)(filePath, "utf8")) - .replace("{{ERROR}}", error) - .replace("display:none", error ? "display:block" : "display:none") - .replace('value=""', `value="${payload && payload.password || ""}"`); - return { content, filePath }; - } - - private ensureGet(request: http.IncomingMessage): void { - if (request.method !== "GET") { - throw new HttpError(`Unsupported method ${request.method}`, HttpCode.BadRequest); - } - } - - private getData(request: http.IncomingMessage): Promise { - return request.method === "POST" - ? new Promise((resolve, reject) => { - let body = ""; - const onEnd = (): void => { - off(); - resolve(querystring.parse(body) as T); - }; - const onError = (error: Error): void => { - off(); - reject(error); - }; - const onData = (d: Buffer): void => { - body += d; - if (body.length > 1e6) { - onError(new HttpError("Payload is too large", HttpCode.LargePayload)); - request.connection.destroy(); - } - }; - const off = (): void => { - request.off("error", onError); - request.off("data", onError); - request.off("end", onEnd); - }; - request.on("error", onError); - request.on("data", onData); - request.on("end", onEnd); - }) - : Promise.resolve({} as T); - } - - private authenticate(request: http.IncomingMessage, payload?: AuthPayload): string | boolean { - if (this.options.auth === "none") { - return true; - } - const safeCompare = localRequire("safe-compare/index"); - if (typeof payload === "undefined") { - payload = this.parseCookies(request); - } - if (this.options.password && payload.key) { - for (let i = 0; i < payload.key.length; ++i) { - if (safeCompare(payload.key[i], this.options.password)) { - return payload.key[i]; - } - } - } - return false; - } - - private parseCookies(request: http.IncomingMessage): T { - const cookies: { [key: string]: string[] } = {}; - if (request.headers.cookie) { - request.headers.cookie.split(";").forEach((keyValue) => { - const [key, value] = split(keyValue, "="); - if (!cookies[key]) { - cookies[key] = []; - } - cookies[key].push(decodeURI(value)); - }); - } - return cookies as T; - } -} - -interface StartPath { - path?: string[] | string; - workspace?: boolean; -} - -interface Settings { - lastVisited?: StartPath; -} - -export class MainServer extends Server { - public readonly _onDidClientConnect = new Emitter(); - public readonly onDidClientConnect = this._onDidClientConnect.event; - private readonly ipc = new IPCServer(this.onDidClientConnect); - - private readonly maxExtraOfflineConnections = 0; - private readonly connections = new Map>(); - - private readonly services = new ServiceCollection(); - private readonly servicesPromise: Promise; - - public readonly _onProxyConnect = new Emitter(); - private proxyPipe = path.join(tmpdir, "tls-proxy"); - private _proxyServer?: Promise; - private readonly proxyTimeout = 5000; - - private settings: Settings = {}; - private heartbeatTimer?: NodeJS.Timeout; - private heartbeatInterval = 60000; - private lastHeartbeat = 0; - - public constructor(options: ServerOptions, args: ParsedArgs) { - super(options); - this.servicesPromise = this.initializeServices(args); - } - - public async listen(): Promise { - const environment = (this.services.get(IEnvironmentService) as EnvironmentService); - const [address] = await Promise.all([ - super.listen(), ...[ - environment.extensionsPath, - ].map((p) => mkdirp(p).then(() => p)), - ]); - return address; - } - - protected async handleWebSocket(socket: net.Socket, parsedUrl: url.UrlWithParsedQuery): Promise { - this.heartbeat(); - if (!parsedUrl.query.reconnectionToken) { - throw new Error("Reconnection token is missing from query parameters"); - } - const protocol = new Protocol(await this.createProxy(socket), { - reconnectionToken: parsedUrl.query.reconnectionToken, - reconnection: parsedUrl.query.reconnection === "true", - skipWebSocketFrames: parsedUrl.query.skipWebSocketFrames === "true", - }); - try { - await this.connect(await protocol.handshake(), protocol); - } catch (error) { - protocol.sendMessage({ type: "error", reason: error.message }); - protocol.dispose(); - protocol.getSocket().dispose(); - } - } - - protected async handleRequest( - base: string, - requestPath: string, - parsedUrl: url.UrlWithParsedQuery, - request: http.IncomingMessage, - ): Promise { - this.heartbeat(); - switch (base) { - case "/": return this.getRoot(request, parsedUrl); - case "/resource": - case "/vscode-remote-resource": - if (typeof parsedUrl.query.path === "string") { - return this.getAnyResource(parsedUrl.query.path); - } - break; - case "/tar": - if (typeof parsedUrl.query.path === "string") { - return this.getTarredResource(parsedUrl.query.path); - } - break; - case "/webview": - if (/^\/vscode-resource/.test(requestPath)) { - return this.getAnyResource(requestPath.replace(/^\/vscode-resource(\/file)?/, "")); - } - return this.getResource( - this.rootPath, - "out/vs/workbench/contrib/webview/browser/pre", - requestPath - ); - } - throw new HttpError("Not found", HttpCode.NotFound); - } - - private async getRoot(request: http.IncomingMessage, parsedUrl: url.UrlWithParsedQuery): Promise { - const remoteAuthority = request.headers.host as string; - const filePath = path.join(this.serverRoot, "browser/workbench.html"); - let [content, startPath] = await Promise.all([ - util.promisify(fs.readFile)(filePath, "utf8"), - this.getFirstValidPath([ - { path: parsedUrl.query.workspace, workspace: true }, - { path: parsedUrl.query.folder, workspace: false }, - (await this.readSettings()).lastVisited, - { path: this.options.openUri } - ], remoteAuthority), - this.servicesPromise, - ]); - - if (startPath) { - this.writeSettings({ - lastVisited: { - path: startPath.uri, - workspace: startPath.workspace - }, - }); - } - - const logger = this.services.get(ILogService) as ILogService; - logger.info("request.url", `"${request.url}"`); - - const transformer = getUriTransformer(remoteAuthority); - - const environment = this.services.get(IEnvironmentService) as IEnvironmentService; - const options: Options = { - WORKBENCH_WEB_CONFIGURATION: { - workspaceUri: startPath && startPath.workspace ? URI.parse(startPath.uri) : undefined, - folderUri: startPath && !startPath.workspace ? URI.parse(startPath.uri) : undefined, - remoteAuthority, - logLevel: getLogLevel(environment), - }, - REMOTE_USER_DATA_URI: transformer.transformOutgoing(URI.file(environment.userDataPath)), - PRODUCT_CONFIGURATION: { - extensionsGallery: product.extensionsGallery, - }, - NLS_CONFIGURATION: await getNlsConfiguration(environment.args.locale || await getLocaleFromConfig(environment.userDataPath), environment.userDataPath), - }; - - if (content) { - content = content.replace(/{{COMMIT}}/g, product.commit || ""); - for (const key in options) { - content = content.replace(`"{{${key}}}"`, `'${JSON.stringify(options[key as keyof Options])}'`); - } - } - - return { content, filePath }; - } - - /** - * Choose the first valid path. If `workspace` is undefined then either a - * workspace or a directory are acceptable. Otherwise it must be a file if a - * workspace or a directory otherwise. - */ - private async getFirstValidPath(startPaths: Array, remoteAuthority: string): Promise<{ uri: string, workspace?: boolean} | undefined> { - const logger = this.services.get(ILogService) as ILogService; - for (let i = 0; i < startPaths.length; ++i) { - const startPath = startPaths[i]; - if (!startPath) { - continue; - } - const paths = typeof startPath.path === "string" ? [startPath.path] : (startPath.path || []); - for (let j = 0; j < paths.length; ++j) { - const uri = url.parse(paths[j]); - try { - if (!uri.pathname) { - throw new Error(`${paths[j]} is not valid`); - } - const stat = await util.promisify(fs.stat)(uri.pathname); - if (typeof startPath.workspace === "undefined" || startPath.workspace !== stat.isDirectory()) { - return { uri: url.format({ - protocol: uri.protocol || "vscode-remote", - hostname: remoteAuthority.split(":")[0], - port: remoteAuthority.split(":")[1], - pathname: uri.pathname, - slashes: true, - }), workspace: !stat.isDirectory() }; - } - } catch (error) { - logger.warn(error.message); - } - } - } - return undefined; - } - - private async connect(message: ConnectionTypeRequest, protocol: Protocol): Promise { - if (product.commit && message.commit !== product.commit) { - throw new Error(`Version mismatch (${message.commit} instead of ${product.commit})`); - } - - switch (message.desiredConnectionType) { - case ConnectionType.ExtensionHost: - case ConnectionType.Management: - if (!this.connections.has(message.desiredConnectionType)) { - this.connections.set(message.desiredConnectionType, new Map()); - } - const connections = this.connections.get(message.desiredConnectionType)!; - - const ok = async () => { - return message.desiredConnectionType === ConnectionType.ExtensionHost - ? { debugPort: await this.getDebugPort() } - : { type: "ok" }; - }; - - const token = protocol.options.reconnectionToken; - if (protocol.options.reconnection && connections.has(token)) { - protocol.sendMessage(await ok()); - const buffer = protocol.readEntireBuffer(); - protocol.dispose(); - return connections.get(token)!.reconnect(protocol.getSocket(), buffer); - } else if (protocol.options.reconnection || connections.has(token)) { - throw new Error(protocol.options.reconnection - ? "Unrecognized reconnection token" - : "Duplicate reconnection token" - ); - } - - protocol.sendMessage(await ok()); - - let connection: Connection; - if (message.desiredConnectionType === ConnectionType.Management) { - connection = new ManagementConnection(protocol, token); - this._onDidClientConnect.fire({ - protocol, onDidClientDisconnect: connection.onClose, - }); - // TODO: Need a way to match clients with a connection. For now - // dispose everything which only works because no extensions currently - // utilize long-running proxies. - (this.services.get(INodeProxyService) as NodeProxyService)._onUp.fire(); - connection.onClose(() => (this.services.get(INodeProxyService) as NodeProxyService)._onDown.fire()); - } else { - const buffer = protocol.readEntireBuffer(); - connection = new ExtensionHostConnection( - message.args ? message.args.language : "en", - protocol, buffer, token, - this.services.get(ILogService) as ILogService, - this.services.get(IEnvironmentService) as IEnvironmentService, - ); - } - connections.set(token, connection); - connection.onClose(() => connections.delete(token)); - this.disposeOldOfflineConnections(connections); - break; - case ConnectionType.Tunnel: return protocol.tunnel(); - default: throw new Error("Unrecognized connection type"); - } - } - - private disposeOldOfflineConnections(connections: Map): void { - const offline = Array.from(connections.values()) - .filter((connection) => typeof connection.offline !== "undefined"); - for (let i = 0, max = offline.length - this.maxExtraOfflineConnections; i < max; ++i) { - offline[i].dispose(); - } - } - - private async initializeServices(args: ParsedArgs): Promise { - const environmentService = new EnvironmentService(args, process.execPath); - const logService = new SpdLogService(RemoteExtensionLogFileName, environmentService.logsPath, getLogLevel(environmentService)); - const fileService = new FileService(logService); - fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(logService)); - - this.allowedRequestPaths.push( - path.join(environmentService.userDataPath, "clp"), // Language packs. - environmentService.extensionsPath, - environmentService.builtinExtensionsPath, - ...environmentService.extraExtensionPaths, - ...environmentService.extraBuiltinExtensionPaths, - ); - - this.ipc.registerChannel("logger", new LoggerChannel(logService)); - this.ipc.registerChannel(ExtensionHostDebugBroadcastChannel.ChannelName, new ExtensionHostDebugBroadcastChannel()); - - this.services.set(ILogService, logService); - this.services.set(IEnvironmentService, environmentService); - this.services.set(IConfigurationService, new SyncDescriptor(ConfigurationService, [environmentService.machineSettingsResource])); - this.services.set(IRequestService, new SyncDescriptor(RequestService)); - this.services.set(IFileService, fileService); - this.services.set(IProductService, { _serviceBrand: undefined, ...product }); - this.services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); - this.services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); - - if (!environmentService.args["disable-telemetry"]) { - this.services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [{ - appender: combinedAppender( - new AppInsightsAppender("code-server", null, () => new TelemetryClient() as any, logService), - new LogAppender(logService), - ), - commonProperties: resolveCommonProperties( - product.commit, product.codeServerVersion, await getMachineId(), - [], environmentService.installSourcePath, "code-server", - ), - piiPaths: this.allowedRequestPaths, - } as ITelemetryServiceConfig])); - } else { - this.services.set(ITelemetryService, NullTelemetryService); - } - - await new Promise((resolve) => { - const instantiationService = new InstantiationService(this.services); - this.services.set(ILocalizationsService, instantiationService.createInstance(LocalizationsService)); - this.services.set(INodeProxyService, instantiationService.createInstance(NodeProxyService)); - - instantiationService.invokeFunction(() => { - instantiationService.createInstance(LogsDataCleaner); - const telemetryService = this.services.get(ITelemetryService) as ITelemetryService; - this.ipc.registerChannel("extensions", new ExtensionManagementChannel( - this.services.get(IExtensionManagementService) as IExtensionManagementService, - (context) => getUriTransformer(context.remoteAuthority), - )); - this.ipc.registerChannel("remoteextensionsenvironment", new ExtensionEnvironmentChannel( - environmentService, logService, telemetryService, this.options.connectionToken || "", - )); - this.ipc.registerChannel("request", new RequestChannel(this.services.get(IRequestService) as IRequestService)); - this.ipc.registerChannel("telemetry", new TelemetryChannel(telemetryService)); - this.ipc.registerChannel("nodeProxy", new NodeProxyChannel(this.services.get(INodeProxyService) as INodeProxyService)); - this.ipc.registerChannel("localizations", >createChannelReceiver(this.services.get(ILocalizationsService) as ILocalizationsService)); - this.ipc.registerChannel("update", new UpdateChannel(instantiationService.createInstance(UpdateService))); - this.ipc.registerChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME, new FileProviderChannel(environmentService, logService)); - resolve(new ErrorTelemetry(telemetryService)); - }); - }); - } - - /** - * TODO: implement. - */ - private async getDebugPort(): Promise { - return undefined; - } - - /** - * Since we can't pass TLS sockets to children, use this to proxy the socket - * and pass a non-TLS socket. - */ - private createProxy = async (socket: net.Socket): Promise => { - if (!(socket instanceof tls.TLSSocket)) { - return socket; - } - - await this.startProxyServer(); - - return new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - listener.dispose(); - socket.destroy(); - proxy.destroy(); - reject(new Error("TLS socket proxy timed out")); - }, this.proxyTimeout); - - const listener = this._onProxyConnect.event((connection) => { - connection.once("data", (data) => { - if (!socket.destroyed && !proxy.destroyed && data.toString() === id) { - clearTimeout(timeout); - listener.dispose(); - [[proxy, socket], [socket, proxy]].forEach(([a, b]) => { - a.pipe(b); - a.on("error", () => b.destroy()); - a.on("close", () => b.destroy()); - a.on("end", () => b.end()); - }); - resolve(connection); - } - }); - }); - - const id = generateUuid(); - const proxy = net.connect(this.proxyPipe); - proxy.once("connect", () => proxy.write(id)); - }); - } - - private async startProxyServer(): Promise { - if (!this._proxyServer) { - this._proxyServer = new Promise(async (resolve) => { - this.proxyPipe = await this.findFreeSocketPath(this.proxyPipe); - await mkdirp(tmpdir); - await rimraf(this.proxyPipe); - const proxyServer = net.createServer((p) => this._onProxyConnect.fire(p)); - proxyServer.once("listening", resolve); - proxyServer.listen(this.proxyPipe); - }); - } - return this._proxyServer; - } - - private async findFreeSocketPath(basePath: string, maxTries: number = 100): Promise { - const canConnect = (path: string): Promise => { - return new Promise((resolve) => { - const socket = net.connect(path); - socket.once("error", () => resolve(false)); - socket.once("connect", () => { - socket.destroy(); - resolve(true); - }); - }); - }; - - let i = 0; - let path = basePath; - while (await canConnect(path) && i < maxTries) { - path = `${basePath}-${++i}`; - } - return path; - } - - /** - * Return the file path for Coder settings. - */ - private get settingsPath(): string { - const environment = this.services.get(IEnvironmentService) as IEnvironmentService; - return path.join(environment.userDataPath, "coder.json"); - } - - /** - * Read settings from the file. On a failure return last known settings and - * log a warning. - * - */ - private async readSettings(): Promise { - try { - const raw = (await util.promisify(fs.readFile)(this.settingsPath, "utf8")).trim(); - this.settings = raw ? JSON.parse(raw) : {}; - } catch (error) { - if (error.code !== "ENOENT") { - (this.services.get(ILogService) as ILogService).warn(error.message); - } - } - return this.settings; - } - - /** - * Write settings combined with current settings. On failure log a warning. - */ - private async writeSettings(newSettings: Partial): Promise { - this.settings = { ...this.settings, ...newSettings }; - try { - await util.promisify(fs.writeFile)(this.settingsPath, JSON.stringify(this.settings)); - } catch (error) { - (this.services.get(ILogService) as ILogService).warn(error.message); - } - } - - /** - * Return the file path for the heartbeat file. - */ - private get heartbeatPath(): string { - const environment = this.services.get(IEnvironmentService) as IEnvironmentService; - return path.join(environment.userDataPath, "heartbeat"); - } - - /** - * Return all online connections regardless of type. - */ - private get onlineConnections(): Connection[] { - const online = []; - this.connections.forEach((connections) => { - connections.forEach((connection) => { - if (typeof connection.offline === "undefined") { - online.push(connection); - } - }); - }); - return online; - } - - /** - * Write to the heartbeat file if we haven't already done so within the - * timeout and start or reset a timer that keeps running as long as there are - * active connections. Failures are logged as warnings. - */ - private heartbeat(): void { - const now = Date.now(); - if (now - this.lastHeartbeat >= this.heartbeatInterval) { - util.promisify(fs.writeFile)(this.heartbeatPath, "").catch((error) => { - (this.services.get(ILogService) as ILogService).warn(error.message); - }); - this.lastHeartbeat = now; - clearTimeout(this.heartbeatTimer!); // We can clear undefined so ! is fine. - this.heartbeatTimer = setTimeout(() => { - if (this.onlineConnections.length > 0) { - this.heartbeat(); - } - }, this.heartbeatInterval); - } - } -} diff --git a/src/node/settings.ts b/src/node/settings.ts new file mode 100644 index 000000000..0d6152b13 --- /dev/null +++ b/src/node/settings.ts @@ -0,0 +1,63 @@ +import * as fs from "fs-extra" +import * as path from "path" +import { extend, xdgLocalDir } from "./util" +import { logger } from "@coder/logger" + +export type Settings = { [key: string]: Settings | string | boolean | number } + +/** + * Provides read and write access to settings. + */ +export class SettingsProvider { + public constructor(private readonly settingsPath: string) {} + + /** + * Read settings from the file. On a failure return last known settings and + * log a warning. + */ + public async read(): Promise { + try { + const raw = (await fs.readFile(this.settingsPath, "utf8")).trim() + return raw ? JSON.parse(raw) : {} + } catch (error) { + if (error.code !== "ENOENT") { + logger.warn(error.message) + } + } + return {} as T + } + + /** + * Write settings combined with current settings. On failure log a warning. + * Objects will be merged and everything else will be replaced. + */ + public async write(settings: Partial): Promise { + try { + await fs.writeFile(this.settingsPath, JSON.stringify(extend(await this.read(), settings), null, 2)) + } catch (error) { + logger.warn(error.message) + } + } +} + +export interface UpdateSettings { + update: { + checked: number + version: string + } +} + +/** + * Global code-server settings. + */ +export interface CoderSettings extends UpdateSettings { + lastVisited: { + url: string + workspace: boolean + } +} + +/** + * Global code-server settings file. + */ +export const settings = new SettingsProvider(path.join(xdgLocalDir, "coder.json")) diff --git a/src/node/socket.ts b/src/node/socket.ts new file mode 100644 index 000000000..e5fe66778 --- /dev/null +++ b/src/node/socket.ts @@ -0,0 +1,110 @@ +import * as fs from "fs-extra" +import * as net from "net" +import * as path from "path" +import * as tls from "tls" +import { Emitter } from "../common/emitter" +import { generateUuid } from "../common/util" +import { tmpdir } from "./util" + +/** + * Provides a way to proxy a TLS socket. Can be used when you need to pass a + * socket to a child process since you can't pass the TLS socket. + */ +export class SocketProxyProvider { + private readonly onProxyConnect = new Emitter() + private proxyPipe = path.join(tmpdir, "tls-proxy") + private _proxyServer?: Promise + private readonly proxyTimeout = 5000 + + /** + * Stop the proxy server. + */ + public stop(): void { + if (this._proxyServer) { + this._proxyServer.then((server) => server.close()) + this._proxyServer = undefined + } + } + + /** + * Create a socket proxy for TLS sockets. If it's not a TLS socket the + * original socket is returned. This will spawn a proxy server on demand. + */ + public async createProxy(socket: net.Socket): Promise { + if (!(socket instanceof tls.TLSSocket)) { + return socket + } + + await this.startProxyServer() + + return new Promise((resolve, reject) => { + const id = generateUuid() + const proxy = net.connect(this.proxyPipe) + proxy.once("connect", () => proxy.write(id)) + + const timeout = setTimeout(() => { + listener.dispose() // eslint-disable-line @typescript-eslint/no-use-before-define + socket.destroy() + proxy.destroy() + reject(new Error("TLS socket proxy timed out")) + }, this.proxyTimeout) + + const listener = this.onProxyConnect.event((connection) => { + connection.once("data", (data) => { + if (!socket.destroyed && !proxy.destroyed && data.toString() === id) { + clearTimeout(timeout) + listener.dispose() + ;[ + [proxy, socket], + [socket, proxy], + ].forEach(([a, b]) => { + a.pipe(b) + a.on("error", () => b.destroy()) + a.on("close", () => b.destroy()) + a.on("end", () => b.end()) + }) + resolve(connection) + } + }) + }) + }) + } + + private async startProxyServer(): Promise { + if (!this._proxyServer) { + this._proxyServer = this.findFreeSocketPath(this.proxyPipe) + .then((pipe) => { + this.proxyPipe = pipe + return Promise.all([fs.mkdirp(tmpdir), fs.remove(this.proxyPipe)]) + }) + .then(() => { + return new Promise((resolve) => { + const proxyServer = net.createServer((p) => this.onProxyConnect.emit(p)) + proxyServer.once("listening", () => resolve(proxyServer)) + proxyServer.listen(this.proxyPipe) + }) + }) + } + return this._proxyServer + } + + public async findFreeSocketPath(basePath: string, maxTries = 100): Promise { + const canConnect = (path: string): Promise => { + return new Promise((resolve) => { + const socket = net.connect(path) + socket.once("error", () => resolve(false)) + socket.once("connect", () => { + socket.destroy() + resolve(true) + }) + }) + } + + let i = 0 + let path = basePath + while ((await canConnect(path)) && i < maxTries) { + path = `${basePath}-${++i}` + } + return path + } +} diff --git a/src/node/update.ts b/src/node/update.ts deleted file mode 100644 index c2056c34a..000000000 --- a/src/node/update.ts +++ /dev/null @@ -1,141 +0,0 @@ -import * as cp from "child_process"; -import * as os from "os"; -import * as path from "path"; -import * as util from "util"; -import { CancellationToken } from "vs/base/common/cancellation"; -import { URI } from "vs/base/common/uri"; -import * as pfs from "vs/base/node/pfs"; -import { IConfigurationService } from "vs/platform/configuration/common/configuration"; -import { IEnvironmentService } from "vs/platform/environment/common/environment"; -import { IFileService } from "vs/platform/files/common/files"; -import { ILogService } from "vs/platform/log/common/log"; -import product from "vs/platform/product/common/product"; -import { asJson, IRequestService } from "vs/platform/request/common/request"; -import { AvailableForDownload, State, UpdateType, StateType } from "vs/platform/update/common/update"; -import { AbstractUpdateService } from "vs/platform/update/electron-main/abstractUpdateService"; -import { ipcMain } from "vs/server/src/node/ipc"; -import { extract } from "vs/server/src/node/marketplace"; -import { tmpdir } from "vs/server/src/node/util"; - -interface IUpdate { - name: string; -} - -export class UpdateService extends AbstractUpdateService { - _serviceBrand: any; - - constructor( - @IConfigurationService configurationService: IConfigurationService, - @IEnvironmentService environmentService: IEnvironmentService, - @IRequestService requestService: IRequestService, - @ILogService logService: ILogService, - @IFileService private readonly fileService: IFileService, - ) { - super(null, configurationService, environmentService, requestService, logService); - } - - /** - * Return true if the currently installed version is the latest. - */ - public async isLatestVersion(latest?: IUpdate | null): Promise { - if (!latest) { - latest = await this.getLatestVersion(); - } - if (latest) { - const latestMajor = parseInt(latest.name); - const currentMajor = parseInt(product.codeServerVersion); - // If these are invalid versions we can't compare meaningfully. - return isNaN(latestMajor) || isNaN(currentMajor) || - // This can happen when there is a pre-release for a new major version. - currentMajor > latestMajor || - // Otherwise assume that if it's not the same then we're out of date. - latest.name === product.codeServerVersion; - } - return true; - } - - protected buildUpdateFeedUrl(quality: string): string { - return `${product.updateUrl}/${quality}`; - } - - public async doQuitAndInstall(): Promise { - if (this.state.type === StateType.Ready) { - ipcMain.relaunch(this.state.update.version); - } - } - - protected async doCheckForUpdates(context: any): Promise { - this.setState(State.CheckingForUpdates(context)); - try { - const update = await this.getLatestVersion(); - if (!update || await this.isLatestVersion(update)) { - this.setState(State.Idle(UpdateType.Archive)); - } else { - this.setState(State.AvailableForDownload({ - version: update.name, - productVersion: update.name, - })); - } - } catch (error) { - this.onRequestError(error, !!context); - } - } - - private async getLatestVersion(): Promise { - const data = await this.requestService.request({ - url: this.url, - headers: { "User-Agent": "code-server" }, - }, CancellationToken.None); - return asJson(data); - } - - protected async doDownloadUpdate(state: AvailableForDownload): Promise { - this.setState(State.Downloading(state.update)); - const target = os.platform(); - const releaseName = await this.buildReleaseName(state.update.version); - const url = "https://github.com/cdr/code-server/releases/download/" - + `${state.update.version}/${releaseName}` - + `.${target === "darwin" ? "zip" : "tar.gz"}`; - const downloadPath = path.join(tmpdir, `${state.update.version}-archive`); - const extractPath = path.join(tmpdir, state.update.version); - try { - await pfs.mkdirp(tmpdir); - const context = await this.requestService.request({ url }, CancellationToken.None, true); - await this.fileService.writeFile(URI.file(downloadPath), context.stream); - await extract(downloadPath, extractPath, undefined, CancellationToken.None); - const newBinary = path.join(extractPath, releaseName, "code-server"); - if (!pfs.exists(newBinary)) { - throw new Error("No code-server binary in extracted archive"); - } - await pfs.unlink(process.argv[0]); // Must unlink first to avoid ETXTBSY. - await pfs.move(newBinary, process.argv[0]); - this.setState(State.Ready(state.update)); - } catch (error) { - this.onRequestError(error, true); - } - await Promise.all([downloadPath, extractPath].map((p) => pfs.rimraf(p))); - } - - private onRequestError(error: Error, showNotification?: boolean): void { - this.logService.error(error); - this.setState(State.Idle(UpdateType.Archive, showNotification ? (error.message || error.toString()) : undefined)); - } - - private async buildReleaseName(release: string): Promise { - let target: string = os.platform(); - if (target === "linux") { - const result = await util.promisify(cp.exec)("ldd --version").catch((error) => ({ - stderr: error.message, - stdout: "", - })); - if (/musl/.test(result.stderr) || /musl/.test(result.stdout)) { - target = "alpine"; - } - } - let arch = os.arch(); - if (arch === "x64") { - arch = "x86_64"; - } - return `code-server${release}-${target}-${arch}`; - } -} diff --git a/src/node/uriTransformer.js b/src/node/uriTransformer.js deleted file mode 100644 index fc69441cf..000000000 --- a/src/node/uriTransformer.js +++ /dev/null @@ -1,24 +0,0 @@ -// This file is included via a regular Node require. I'm not sure how (or if) -// we can write this in Typescript and have it compile to non-AMD syntax. -module.exports = (remoteAuthority) => { - return { - transformIncoming: (uri) => { - switch (uri.scheme) { - case "vscode-remote": return { scheme: "file", path: uri.path }; - default: return uri; - } - }, - transformOutgoing: (uri) => { - switch (uri.scheme) { - case "file": return { scheme: "vscode-remote", authority: remoteAuthority, path: uri.path }; - default: return uri; - } - }, - transformOutgoingScheme: (scheme) => { - switch (scheme) { - case "file": return "vscode-remote"; - default: return scheme; - } - }, - }; -}; diff --git a/src/node/util.ts b/src/node/util.ts index f5dae6bd2..44e27be05 100644 --- a/src/node/util.ts +++ b/src/node/util.ts @@ -1,144 +1,196 @@ -import * as cp from "child_process"; -import * as crypto from "crypto"; -import * as fs from "fs"; -import * as os from "os"; -import * as path from "path"; -import * as util from "util"; -import * as rg from "vscode-ripgrep"; +import * as cp from "child_process" +import * as crypto from "crypto" +import * as fs from "fs-extra" +import * as os from "os" +import * as path from "path" +import * as util from "util" -import { getPathFromAmdModule } from "vs/base/common/amd"; -import { getMediaMime as vsGetMediaMime } from "vs/base/common/mime"; -import { extname } from "vs/base/common/path"; -import { URITransformer, IRawURITransformer } from "vs/base/common/uriIpc"; -import { mkdirp } from "vs/base/node/pfs"; +export const tmpdir = path.join(os.tmpdir(), "code-server") -export enum AuthType { - Password = "password", - None = "none", +const getXdgDataDir = (): string => { + switch (process.platform) { + case "win32": + return path.join(process.env.XDG_DATA_HOME || path.join(os.homedir(), "AppData/Local"), "code-server/Data") + case "darwin": + return path.join( + process.env.XDG_DATA_HOME || path.join(os.homedir(), "Library/Application Support"), + "code-server", + ) + default: + return path.join(process.env.XDG_DATA_HOME || path.join(os.homedir(), ".local/share"), "code-server") + } } -export enum FormatType { - Json = "json", +export const xdgLocalDir = getXdgDataDir() + +export const generateCertificate = async (): Promise<{ cert: string; certKey: string }> => { + const paths = { + cert: path.join(tmpdir, "self-signed.cert"), + certKey: path.join(tmpdir, "self-signed.key"), + } + const checks = await Promise.all([fs.pathExists(paths.cert), fs.pathExists(paths.certKey)]) + if (!checks[0] || !checks[1]) { + // Require on demand so openssl isn't required if you aren't going to + // generate certificates. + const pem = require("pem") as typeof import("pem") + const certs = await new Promise((resolve, reject): void => { + pem.createCertificate({ selfSigned: true }, (error, result) => { + return error ? reject(error) : resolve(result) + }) + }) + await fs.mkdirp(tmpdir) + await Promise.all([fs.writeFile(paths.cert, certs.certificate), fs.writeFile(paths.certKey, certs.serviceKey)]) + } + return paths } -export const tmpdir = path.join(os.tmpdir(), "code-server"); - -export const generateCertificate = async (): Promise<{ cert: string, certKey: string }> => { - const paths = { - cert: path.join(tmpdir, "self-signed.cert"), - certKey: path.join(tmpdir, "self-signed.key"), - }; - - const exists = await Promise.all([ - util.promisify(fs.exists)(paths.cert), - util.promisify(fs.exists)(paths.certKey), - ]); - - if (!exists[0] || !exists[1]) { - const pem = localRequire("pem/lib/pem"); - const certs = await new Promise((resolve, reject): void => { - pem.createCertificate({ selfSigned: true }, (error, result) => { - if (error) { - return reject(error); - } - resolve(result); - }); - }); - await mkdirp(tmpdir); - await Promise.all([ - util.promisify(fs.writeFile)(paths.cert, certs.certificate), - util.promisify(fs.writeFile)(paths.certKey, certs.serviceKey), - ]); - } - - return paths; -}; - -export const uriTransformerPath = getPathFromAmdModule(require, "vs/server/src/node/uriTransformer"); -export const getUriTransformer = (remoteAuthority: string): URITransformer => { - const rawURITransformerFactory = require.__$__nodeRequire(uriTransformerPath); - const rawURITransformer = rawURITransformerFactory(remoteAuthority); - return new URITransformer(rawURITransformer); -}; - -export const generatePassword = async (length: number = 24): Promise => { - const buffer = Buffer.alloc(Math.ceil(length / 2)); - await util.promisify(crypto.randomFill)(buffer); - return buffer.toString("hex").substring(0, length); -}; +export const generatePassword = async (length = 24): Promise => { + const buffer = Buffer.alloc(Math.ceil(length / 2)) + await util.promisify(crypto.randomFill)(buffer) + return buffer.toString("hex").substring(0, length) +} export const hash = (str: string): string => { - return crypto.createHash("sha256").update(str).digest("hex"); -}; + return crypto + .createHash("sha256") + .update(str) + .digest("hex") +} + +const mimeTypes: { [key: string]: string } = { + ".aac": "audio/x-aac", + ".avi": "video/x-msvideo", + ".bmp": "image/bmp", + ".css": "text/css", + ".flv": "video/x-flv", + ".gif": "image/gif", + ".html": "text/html", + ".ico": "image/x-icon", + ".jpe": "image/jpg", + ".jpeg": "image/jpg", + ".jpg": "image/jpg", + ".js": "application/javascript", + ".json": "application/json", + ".m1v": "video/mpeg", + ".m2a": "audio/mpeg", + ".m2v": "video/mpeg", + ".m3a": "audio/mpeg", + ".mid": "audio/midi", + ".midi": "audio/midi", + ".mk3d": "video/x-matroska", + ".mks": "video/x-matroska", + ".mkv": "video/x-matroska", + ".mov": "video/quicktime", + ".movie": "video/x-sgi-movie", + ".mp2": "audio/mpeg", + ".mp2a": "audio/mpeg", + ".mp3": "audio/mpeg", + ".mp4": "video/mp4", + ".mp4a": "audio/mp4", + ".mp4v": "video/mp4", + ".mpe": "video/mpeg", + ".mpeg": "video/mpeg", + ".mpg": "video/mpeg", + ".mpg4": "video/mp4", + ".mpga": "audio/mpeg", + ".oga": "audio/ogg", + ".ogg": "audio/ogg", + ".ogv": "video/ogg", + ".png": "image/png", + ".psd": "image/vnd.adobe.photoshop", + ".qt": "video/quicktime", + ".spx": "audio/ogg", + ".svg": "image/svg+xml", + ".tga": "image/x-tga", + ".tif": "image/tiff", + ".tiff": "image/tiff", + ".txt": "text/plain", + ".wav": "audio/x-wav", + ".wasm": "application/wasm", + ".webm": "video/webm", + ".webp": "image/webp", + ".wma": "audio/x-ms-wma", + ".wmv": "video/x-ms-wmv", + ".woff": "application/font-woff", +} export const getMediaMime = (filePath?: string): string => { - return filePath && (vsGetMediaMime(filePath) || (<{[index: string]: string}>{ - ".css": "text/css", - ".html": "text/html", - ".js": "application/javascript", - ".json": "application/json", - })[extname(filePath)]) || "text/plain"; -}; + return (filePath && mimeTypes[path.extname(filePath)]) || "text/plain" +} export const isWsl = async (): Promise => { - return process.platform === "linux" - && os.release().toLowerCase().indexOf("microsoft") !== -1 - || (await util.promisify(fs.readFile)("/proc/version", "utf8")) - .toLowerCase().indexOf("microsoft") !== -1; -}; + return ( + (process.platform === "linux" && + os + .release() + .toLowerCase() + .indexOf("microsoft") !== -1) || + (await fs.readFile("/proc/version", "utf8")).toLowerCase().indexOf("microsoft") !== -1 + ) +} +/** + * Try opening a URL using whatever the system has set for opening URLs. + */ export const open = async (url: string): Promise => { - const args = []; - const options = {}; - const platform = await isWsl() ? "wsl" : process.platform; - let command = platform === "darwin" ? "open" : "xdg-open"; - if (platform === "win32" || platform === "wsl") { - command = platform === "wsl" ? "cmd.exe" : "cmd"; - args.push("/c", "start", '""', "/b"); - url = url.replace(/&/g, "^&"); - } - const proc = cp.spawn(command, [...args, url], options); - await new Promise((resolve, reject) => { - proc.on("error", reject); - proc.on("close", (code) => { - return code !== 0 - ? reject(new Error(`Failed to open with code ${code}`)) - : resolve(); - }); - }); -}; + const args = [] as string[] + const options = {} as cp.SpawnOptions + const platform = (await isWsl()) ? "wsl" : process.platform + let command = platform === "darwin" ? "open" : "xdg-open" + if (platform === "win32" || platform === "wsl") { + command = platform === "wsl" ? "cmd.exe" : "cmd" + args.push("/c", "start", '""', "/b") + url = url.replace(/&/g, "^&") + } + const proc = cp.spawn(command, [...args, url], options) + await new Promise((resolve, reject) => { + proc.on("error", reject) + proc.on("close", (code) => { + return code !== 0 ? reject(new Error(`Failed to open with code ${code}`)) : resolve() + }) + }) +} /** - * Extract executables to the temporary directory. This is required since we - * can't execute binaries stored within our binary. + * For iterating over an enum's values. */ -export const unpackExecutables = async (): Promise => { - const rgPath = (rg as any).binaryRgPath; - const destination = path.join(tmpdir, path.basename(rgPath || "")); - if (rgPath && !(await util.promisify(fs.exists)(destination))) { - await mkdirp(tmpdir); - await util.promisify(fs.writeFile)(destination, await util.promisify(fs.readFile)(rgPath)); - await util.promisify(fs.chmod)(destination, "755"); - } -}; - +// eslint-disable-next-line @typescript-eslint/no-explicit-any export const enumToArray = (t: any): string[] => { - const values = []; - for (const k in t) { - values.push(t[k]); - } - return values; -}; - -export const buildAllowedMessage = (t: any): string => { - const values = enumToArray(t); - return `Allowed value${values.length === 1 ? " is" : "s are"} ${values.map((t) => `'${t}'`).join(", ")}`; -}; + const values = [] as string[] + for (const k in t) { + values.push(t[k]) + } + return values +} /** - * Require a local module. This is necessary since VS Code's loader only looks - * at the root for Node modules. + * For displaying all allowed options in an enum. */ -export const localRequire = (modulePath: string): T => { - return require.__$__nodeRequire(path.resolve(__dirname, "../../node_modules", modulePath)); -}; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const buildAllowedMessage = (t: any): string => { + const values = enumToArray(t) + return `Allowed value${values.length === 1 ? " is" : "s are"} ${values.map((t) => `'${t}'`).join(", ")}` +} + +export const isObject = (obj: T): obj is T => { + return !Array.isArray(obj) && typeof obj === "object" && obj !== null +} + +/** + * Extend a with b and return a new object. Properties with objects will be + * recursively merged while all other properties are just overwritten. + */ +export function extend(a: A, b: B): A & B +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function extend(...args: any[]): any { + const c = {} as any // eslint-disable-line @typescript-eslint/no-explicit-any + for (const obj of args) { + if (!isObject(obj)) { + continue + } + for (const key in obj) { + c[key] = isObject(obj[key]) ? extend(c[key], obj[key]) : obj[key] + } + } + return c +} diff --git a/src/node/wrapper.ts b/src/node/wrapper.ts new file mode 100644 index 000000000..0733c3781 --- /dev/null +++ b/src/node/wrapper.ts @@ -0,0 +1,235 @@ +import { logger, field } from "@coder/logger" +import * as cp from "child_process" +import { Emitter } from "../common/emitter" + +interface HandshakeMessage { + type: "handshake" +} + +interface RelaunchMessage { + type: "relaunch" + version: string +} + +export type Message = RelaunchMessage | HandshakeMessage + +export class ProcessError extends Error { + public constructor(message: string, public readonly code: number | undefined) { + super(message) + this.name = this.constructor.name + Error.captureStackTrace(this, this.constructor) + } +} + +/** + * Allows the wrapper and inner processes to communicate. + */ +export class IpcMain { + private readonly _onMessage = new Emitter() + public readonly onMessage = this._onMessage.event + private readonly _onDispose = new Emitter() + public readonly onDispose = this._onDispose.event + public readonly exit: (code?: number) => never + + public constructor(public readonly parentPid?: number) { + process.on("SIGINT", () => this._onDispose.emit("SIGINT")) + process.on("SIGTERM", () => this._onDispose.emit("SIGTERM")) + process.on("exit", () => this._onDispose.emit(undefined)) + + // Ensure we control when the process exits. + this.exit = process.exit + process.exit = function(code?: number) { + logger.warn(`process.exit() was prevented: ${code || "unknown code"}.`) + } as (code?: number) => never + + this.onDispose((signal) => { + // Remove listeners to avoid possibly triggering disposal again. + process.removeAllListeners() + + // Let any other handlers run first then exit. + logger.debug(`${parentPid ? "inner process" : "wrapper"} ${process.pid} disposing`, field("code", signal)) + setTimeout(() => this.exit(0), 0) + }) + + // Kill the inner process if the parent dies. This is for the case where the + // parent process is forcefully terminated and cannot clean up. + if (parentPid) { + setInterval(() => { + try { + // process.kill throws an exception if the process doesn't exist. + process.kill(parentPid, 0) + } catch (_) { + // Consider this an error since it should have been able to clean up + // the child process unless it was forcefully killed. + logger.error(`parent process ${parentPid} died`) + this._onDispose.emit(undefined) + } + }, 5000) + } + } + + public handshake(child?: cp.ChildProcess): Promise { + return new Promise((resolve, reject) => { + const target = child || process + const onMessage = (message: Message): void => { + logger.debug( + `${child ? "wrapper" : "inner process"} ${process.pid} received message from ${ + child ? child.pid : this.parentPid + }`, + field("message", message), + ) + if (message.type === "handshake") { + target.removeListener("message", onMessage) + target.on("message", (msg) => this._onMessage.emit(msg)) + // The wrapper responds once the inner process starts the handshake. + if (child) { + if (!target.send) { + throw new Error("child not spawned with IPC") + } + target.send({ type: "handshake" }) + } + resolve() + } + } + target.on("message", onMessage) + if (child) { + child.once("error", reject) + child.once("exit", (code) => { + reject(new ProcessError(`Unexpected exit with code ${code}`, code !== null ? code : undefined)) + }) + } else { + // The inner process initiates the handshake. + this.send({ type: "handshake" }) + } + }) + } + + public relaunch(version: string): void { + this.send({ type: "relaunch", version }) + } + + private send(message: Message): void { + if (!process.send) { + throw new Error("not spawned with IPC") + } + process.send(message) + } +} + +let _ipcMain: IpcMain +export const ipcMain = (): IpcMain => { + if (!_ipcMain) { + _ipcMain = new IpcMain( + typeof process.env.CODE_SERVER_PARENT_PID !== "undefined" + ? parseInt(process.env.CODE_SERVER_PARENT_PID) + : undefined, + ) + } + return _ipcMain +} + +export interface WrapperOptions { + maxMemory?: number + nodeOptions?: string +} + +/** + * Provides a way to wrap a process for the purpose of updating the running + * instance. + */ +export class WrapperProcess { + private process?: cp.ChildProcess + private started?: Promise + + public constructor(private currentVersion: string, private readonly options?: WrapperOptions) { + ipcMain().onDispose(() => { + if (this.process) { + this.process.removeAllListeners() + this.process.kill() + } + }) + + ipcMain().onMessage(async (message) => { + switch (message.type) { + case "relaunch": + logger.info(`Relaunching: ${this.currentVersion} -> ${message.version}`) + this.currentVersion = message.version + this.started = undefined + if (this.process) { + this.process.removeAllListeners() + this.process.kill() + } + try { + await this.start() + } catch (error) { + logger.error(error.message) + ipcMain().exit(typeof error.code === "number" ? error.code : 1) + } + break + default: + logger.error(`Unrecognized message ${message}`) + break + } + }) + } + + public start(): Promise { + if (!this.started) { + this.started = this.spawn().then((child) => { + logger.debug(`spawned inner process ${child.pid}`) + ipcMain() + .handshake(child) + .then(() => { + child.once("exit", (code) => { + logger.debug(`inner process ${child.pid} exited unexpectedly`) + ipcMain().exit(code || 0) + }) + }) + this.process = child + }) + } + return this.started + } + + private async spawn(): Promise { + // Flags to pass along to the Node binary. + let nodeOptions = `${process.env.NODE_OPTIONS || ""} ${(this.options && this.options.nodeOptions) || ""}` + if (!/max_old_space_size=(\d+)/g.exec(nodeOptions)) { + nodeOptions += ` --max_old_space_size=${(this.options && this.options.maxMemory) || 2048}` + } + + // Use spawn (instead of fork) to use the new binary in case it was updated. + return cp.spawn(process.argv[0], process.argv.slice(1), { + env: { + ...process.env, + CODE_SERVER_PARENT_PID: process.pid.toString(), + NODE_OPTIONS: nodeOptions, + }, + stdio: ["inherit", "inherit", "inherit", "ipc"], + }) + } +} + +// It's possible that the pipe has closed (for example if you run code-server +// --version | head -1). Assume that means we're done. +if (!process.stdout.isTTY) { + process.stdout.on("error", () => ipcMain().exit()) +} + +export const wrap = (fn: () => Promise): void => { + if (ipcMain().parentPid) { + ipcMain() + .handshake() + .then(() => fn()) + .catch((error: ProcessError): void => { + logger.error(error.message) + ipcMain().exit(typeof error.code === "number" ? error.code : 1) + }) + } else { + const wrapper = new WrapperProcess(require("../../package.json").version) + wrapper.start().catch((error) => { + logger.error(error.message) + ipcMain().exit(typeof error.code === "number" ? error.code : 1) + }) + } +} diff --git a/test/cli.test.ts b/test/cli.test.ts new file mode 100644 index 000000000..9de3900eb --- /dev/null +++ b/test/cli.test.ts @@ -0,0 +1,163 @@ +import * as assert from "assert" +import * as path from "path" +import { parse } from "../src/node/cli" +import { xdgLocalDir } from "../src/node/util" + +describe("cli", () => { + beforeEach(() => { + delete process.env.LOG_LEVEL + }) + + it("should set defaults", () => { + assert.deepEqual(parse([]), { + _: [], + "extensions-dir": path.join(xdgLocalDir, "extensions"), + "user-data-dir": xdgLocalDir, + }) + }) + + it("should parse all available options", () => { + assert.deepEqual( + parse([ + "--auth", + "none", + "--extensions-dir", + "foo", + "--builtin-extensions-dir", + "foobar", + "--extra-extensions-dir", + "nozzle", + "1", + "--extra-builtin-extensions-dir", + "bazzle", + "--verbose", + "2", + "--log", + "error", + "--help", + "--open", + "--socket=mumble", + "3", + "--user-data-dir", + "bar", + "--cert=baz", + "--cert-key", + "qux", + "--version", + "--json", + "--port=8081", + "--host", + "0.0.0.0", + "4", + "--", + "-5", + "--6", + ]), + { + _: ["1", "2", "3", "4", "-5", "--6"], + auth: "none", + "builtin-extensions-dir": path.resolve("foobar"), + "cert-key": path.resolve("qux"), + cert: { + value: path.resolve("baz"), + }, + "extensions-dir": path.resolve("foo"), + "extra-builtin-extensions-dir": [path.resolve("bazzle")], + "extra-extensions-dir": [path.resolve("nozzle")], + help: true, + host: "0.0.0.0", + json: true, + log: "trace", + open: true, + port: 8081, + socket: path.resolve("mumble"), + "user-data-dir": path.resolve("bar"), + verbose: true, + version: true, + }, + ) + }) + + it("should work with short options", () => { + assert.deepEqual(parse(["-vvv", "-v"]), { + _: [], + "extensions-dir": path.join(xdgLocalDir, "extensions"), + "user-data-dir": xdgLocalDir, + log: "trace", + verbose: true, + version: true, + }) + assert.equal(process.env.LOG_LEVEL, "trace") + }) + + it("should use log level env var", () => { + process.env.LOG_LEVEL = "debug" + assert.deepEqual(parse([]), { + _: [], + "extensions-dir": path.join(xdgLocalDir, "extensions"), + "user-data-dir": xdgLocalDir, + log: "debug", + }) + assert.equal(process.env.LOG_LEVEL, "debug") + }) + + it("should prefer --log to env var", () => { + process.env.LOG_LEVEL = "debug" + assert.deepEqual(parse(["--log", "info"]), { + _: [], + "extensions-dir": path.join(xdgLocalDir, "extensions"), + "user-data-dir": xdgLocalDir, + log: "info", + }) + assert.equal(process.env.LOG_LEVEL, "info") + }) + + it("should error if value isn't provided", () => { + assert.throws(() => parse(["--auth"]), /--auth requires a value/) + assert.throws(() => parse(["--auth=", "--log=debug"]), /--auth requires a value/) + assert.throws(() => parse(["--auth", "--log"]), /--auth requires a value/) + assert.throws(() => parse(["--auth", "--invalid"]), /--auth requires a value/) + }) + + it("should error if value is invalid", () => { + assert.throws(() => parse(["--port", "foo"]), /--port must be a number/) + assert.throws(() => parse(["--auth", "invalid"]), /--auth valid values: \[password, none\]/) + assert.throws(() => parse(["--log", "invalid"]), /--log valid values: \[trace, debug, info, warn, error\]/) + }) + + it("should error if the option doesn't exist", () => { + assert.throws(() => parse(["--foo"]), /Unknown option --foo/) + }) + + it("should not error if the value is optional", () => { + assert.deepEqual(parse(["--cert"]), { + _: [], + "extensions-dir": path.join(xdgLocalDir, "extensions"), + "user-data-dir": xdgLocalDir, + cert: { + value: undefined, + }, + }) + }) + + it("should not allow option-like values", () => { + assert.throws(() => parse(["--socket", "--socket-path-value"]), /--socket requires a value/) + // If you actually had a path like this you would do this instead: + assert.deepEqual(parse(["--socket", "./--socket-path-value"]), { + _: [], + "extensions-dir": path.join(xdgLocalDir, "extensions"), + "user-data-dir": xdgLocalDir, + socket: path.resolve("--socket-path-value"), + }) + assert.throws(() => parse(["--cert", "--socket-path-value"]), /Unknown option --socket-path-value/) + }) + + it("should allow positional arguments before options", () => { + assert.deepEqual(parse(["foo", "test", "--auth", "none"]), { + _: ["foo", "test"], + "extensions-dir": path.join(xdgLocalDir, "extensions"), + "user-data-dir": xdgLocalDir, + auth: "none", + }) + }) +}) diff --git a/test/socket.test.ts b/test/socket.test.ts new file mode 100644 index 000000000..c1312d824 --- /dev/null +++ b/test/socket.test.ts @@ -0,0 +1,124 @@ +import { field, logger } from "@coder/logger" +import * as assert from "assert" +import * as fs from "fs-extra" +import "leaked-handles" +import * as net from "net" +import * as path from "path" +import * as tls from "tls" +import { Emitter } from "../src/common/emitter" +import { generateCertificate, tmpdir } from "../src/node/util" +import { SocketProxyProvider } from "../src/node/socket" + +describe("SocketProxyProvider", () => { + const provider = new SocketProxyProvider() + + const onServerError = new Emitter<{ event: string; error: Error }>() + const onClientError = new Emitter<{ event: string; error: Error }>() + const onProxyError = new Emitter<{ event: string; error: Error }>() + const fromServerToClient = new Emitter() + const fromClientToServer = new Emitter() + const fromClientToProxy = new Emitter() + + let errors = 0 + let close = false + const onError = ({ event, error }: { event: string; error: Error }): void => { + if (!close || event === "error") { + logger.error(event, field("error", error.message)) + ++errors + } + } + onServerError.event(onError) + onClientError.event(onError) + onProxyError.event(onError) + + let server: tls.TLSSocket + let proxy: net.Socket + let client: tls.TLSSocket + + const getData = (emitter: Emitter): Promise => { + return new Promise((resolve) => { + const d = emitter.event((t) => { + d.dispose() + resolve(t) + }) + }) + } + + before(async () => { + const cert = await generateCertificate() + const options = { + cert: fs.readFileSync(cert.cert), + key: fs.readFileSync(cert.certKey), + rejectUnauthorized: false, + } + + await fs.mkdirp(path.join(tmpdir, "tests")) + const socketPath = await provider.findFreeSocketPath(path.join(tmpdir, "tests/tls-socket-proxy")) + await fs.remove(socketPath) + + return new Promise((_resolve) => { + const resolved: { [key: string]: boolean } = { client: false, server: false } + const resolve = (type: "client" | "server"): void => { + resolved[type] = true + if (resolved.client && resolved.server) { + // We don't need any more connections. + main.close() // eslint-disable-line @typescript-eslint/no-use-before-define + _resolve() + } + } + const main = tls + .createServer(options, (s) => { + server = s + server + .on("data", (d) => fromClientToServer.emit(d)) + .on("error", (error) => onServerError.emit({ event: "error", error })) + .on("end", () => onServerError.emit({ event: "end", error: new Error("unexpected end") })) + .on("close", () => onServerError.emit({ event: "close", error: new Error("unexpected close") })) + resolve("server") + }) + .on("error", (error) => onServerError.emit({ event: "error", error })) + .on("end", () => onServerError.emit({ event: "end", error: new Error("unexpected end") })) + .on("close", () => onServerError.emit({ event: "close", error: new Error("unexpected close") })) + .listen(socketPath, () => { + client = tls + .connect({ ...options, path: socketPath }) + .on("data", (d) => fromServerToClient.emit(d)) + .on("error", (error) => onClientError.emit({ event: "error", error })) + .on("end", () => onClientError.emit({ event: "end", error: new Error("unexpected end") })) + .on("close", () => onClientError.emit({ event: "close", error: new Error("unexpected close") })) + .once("connect", () => resolve("client")) + }) + }) + }) + + it("should work without a proxy", async () => { + server.write("server->client") + assert.equal(await getData(fromServerToClient), "server->client") + client.write("client->server") + assert.equal(await getData(fromClientToServer), "client->server") + assert.equal(errors, 0) + }) + + it("should work with a proxy", async () => { + assert.equal(server instanceof tls.TLSSocket, true) + proxy = (await provider.createProxy(server)) + .on("data", (d) => fromClientToProxy.emit(d)) + .on("error", (error) => onProxyError.emit({ event: "error", error })) + .on("end", () => onProxyError.emit({ event: "end", error: new Error("unexpected end") })) + .on("close", () => onProxyError.emit({ event: "close", error: new Error("unexpected close") })) + + provider.stop() // We don't need more proxies. + + proxy.write("server proxy->client") + assert.equal(await getData(fromServerToClient), "server proxy->client") + client.write("client->server proxy") + assert.equal(await getData(fromClientToProxy), "client->server proxy") + assert.equal(errors, 0) + }) + + it("should close", async () => { + close = true + client.end() + proxy.end() + }) +}) diff --git a/test/tsconfig.json b/test/tsconfig.json new file mode 100644 index 000000000..5197ce276 --- /dev/null +++ b/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["./**/*.ts"] +} diff --git a/test/update.test.ts b/test/update.test.ts new file mode 100644 index 000000000..903c9df78 --- /dev/null +++ b/test/update.test.ts @@ -0,0 +1,222 @@ +import zip from "adm-zip" +import * as assert from "assert" +import * as fs from "fs-extra" +import * as http from "http" +import * as path from "path" +import * as tar from "tar-fs" +import * as zlib from "zlib" +import { LatestResponse, UpdateHttpProvider } from "../src/node/app/update" +import { AuthType } from "../src/node/http" +import { SettingsProvider, UpdateSettings } from "../src/node/settings" +import { tmpdir } from "../src/node/util" + +describe("update", () => { + const archivePath = path.join(tmpdir, "tests/updates/code-server-loose-source") + let version = "1.0.0" + let spy: string[] = [] + const server = http.createServer((request: http.IncomingMessage, response: http.ServerResponse) => { + if (!request.url) { + throw new Error("no url") + } + spy.push(request.url) + response.writeHead(200) + if (request.url === "/latest") { + const latest: LatestResponse = { + name: version, + } + return response.end(JSON.stringify(latest)) + } + + const path = archivePath + (request.url.endsWith(".tar.gz") ? ".tar.gz" : ".zip") + + const stream = fs.createReadStream(path) + stream.on("error", (error: NodeJS.ErrnoException) => { + response.writeHead(500) + response.end(error.message) + }) + response.writeHead(200) + stream.on("close", () => response.end()) + stream.pipe(response) + }) + + const jsonPath = path.join(tmpdir, "tests/updates/update.json") + const settings = new SettingsProvider(jsonPath) + + let _provider: UpdateHttpProvider | undefined + const provider = (): UpdateHttpProvider => { + if (!_provider) { + const address = server.address() + if (!address || typeof address === "string" || !address.port) { + throw new Error("unexpected address") + } + _provider = new UpdateHttpProvider( + { + auth: AuthType.None, + base: "/update", + commit: "test", + }, + true, + `http://${address.address}:${address.port}/latest`, + `http://${address.address}:${address.port}/download/{{VERSION}}/{{RELEASE_NAME}}`, + settings, + ) + } + return _provider + } + + before(async () => { + const archiveName = "code-server-9999999.99999.9999-linux-x86_64" + await fs.remove(path.join(tmpdir, "tests/updates")) + await fs.mkdirp(path.join(archivePath, archiveName)) + + await Promise.all([ + fs.writeFile(path.join(archivePath, archiveName, "code-server"), `console.log("UPDATED")`), + fs.writeFile(path.join(archivePath, archiveName, "node"), `NODE BINARY`), + ]) + + await Promise.all([ + new Promise((resolve, reject) => { + const write = fs.createWriteStream(archivePath + ".tar.gz") + const compress = zlib.createGzip() + compress.pipe(write) + compress.on("error", (error) => compress.destroy(error)) + compress.on("close", () => write.end()) + tar.pack(archivePath).pipe(compress) + write.on("close", reject) + write.on("finish", () => { + resolve() + }) + }), + new Promise((resolve, reject) => { + const zipFile = new zip() + zipFile.addLocalFolder(archivePath) + zipFile.writeZip(archivePath + ".zip", (error) => { + return error ? reject(error) : resolve(error) + }) + }), + ]) + + await new Promise((resolve, reject) => { + server.on("error", reject) + server.on("listening", resolve) + server.listen({ + port: 0, + host: "localhost", + }) + }) + }) + + after(() => { + server.close() + }) + + beforeEach(() => { + spy = [] + }) + + it("should get the latest", async () => { + version = "2.1.0" + + const p = provider() + const now = Date.now() + const update = await p.getUpdate() + + assert.deepEqual({ update }, await settings.read()) + assert.equal(isNaN(update.checked), false) + assert.equal(update.checked < Date.now() && update.checked >= now, true) + assert.equal(update.version, "2.1.0") + assert.deepEqual(spy, ["/latest"]) + }) + + it("should keep existing information", async () => { + version = "3.0.1" + + const p = provider() + const now = Date.now() + const update = await p.getUpdate() + + assert.deepEqual({ update }, await settings.read()) + assert.equal(isNaN(update.checked), false) + assert.equal(update.checked < now, true) + assert.equal(update.version, "2.1.0") + assert.deepEqual(spy, []) + }) + + it("should force getting the latest", async () => { + version = "4.1.1" + + const p = provider() + const now = Date.now() + const update = await p.getUpdate(true) + + assert.deepEqual({ update }, await settings.read()) + assert.equal(isNaN(update.checked), false) + assert.equal(update.checked < Date.now() && update.checked >= now, true) + assert.equal(update.version, "4.1.1") + assert.deepEqual(spy, ["/latest"]) + }) + + it("should get latest after interval passes", async () => { + const p = provider() + await p.getUpdate() + assert.deepEqual(spy, []) + + let checked = Date.now() - 1000 * 60 * 60 * 23 + await settings.write({ update: { checked, version } }) + await p.getUpdate() + assert.deepEqual(spy, []) + + checked = Date.now() - 1000 * 60 * 60 * 25 + await settings.write({ update: { checked, version } }) + + const update = await p.getUpdate() + assert.notEqual(update.checked, checked) + assert.deepEqual(spy, ["/latest"]) + }) + + it("should check if it's the current version", async () => { + version = "9999999.99999.9999" + + const p = provider() + let update = await p.getUpdate(true) + assert.equal(p.isLatestVersion(update), false) + + version = "0.0.0" + update = await p.getUpdate(true) + assert.equal(p.isLatestVersion(update), true) + + // Old version format; make sure it doesn't report as being later. + version = "999999.9999-invalid999.99.9" + update = await p.getUpdate(true) + assert.equal(p.isLatestVersion(update), true) + }) + + it("should download and apply an update", async () => { + version = "9999999.99999.9999" + + const p = provider() + const update = await p.getUpdate(true) + + // Create an existing version. + const destination = path.join(tmpdir, "tests/updates/code-server") + await fs.mkdirp(destination) + const entry = path.join(destination, "code-server") + await fs.writeFile(entry, `console.log("OLD")`) + assert.equal(`console.log("OLD")`, await fs.readFile(entry, "utf8")) + + // Updating should replace the existing version. + await p.downloadAndApplyUpdate(update, destination, "linux") + assert.equal(`console.log("UPDATED")`, await fs.readFile(entry, "utf8")) + + // Should still work if there is no existing version somehow. + await fs.remove(destination) + await p.downloadAndApplyUpdate(update, destination, "linux") + assert.equal(`console.log("UPDATED")`, await fs.readFile(entry, "utf8")) + + assert.deepEqual(spy, [ + "/latest", + `/download/${version}/code-server-${version}-linux-x86_64.tar.gz`, + `/download/${version}/code-server-${version}-linux-x86_64.tar.gz`, + ]) + }) +}) diff --git a/test/util.test.ts b/test/util.test.ts new file mode 100644 index 000000000..28fb9fc8c --- /dev/null +++ b/test/util.test.ts @@ -0,0 +1,55 @@ +import * as assert from "assert" +import { normalize } from "../src/common/util" +import { extend } from "../src/node/util" + +describe("util", () => { + describe("extend", () => { + it("should extend", () => { + const a = { foo: { bar: 0, baz: 2 }, garply: 4, waldo: 6 } + const b = { foo: { bar: 1, qux: 3 }, garply: "5", fred: 7 } + const extended = extend(a, b) + assert.deepEqual(extended, { + foo: { bar: 1, baz: 2, qux: 3 }, + garply: "5", + waldo: 6, + fred: 7, + }) + }) + + it("should make deep copies of the original objects", () => { + const a = { foo: 0, bar: { frobnozzle: 2 }, mumble: { qux: { thud: 4 } } } + const b = { foo: 1, bar: { chad: 3 } } + const extended = extend(a, b) + assert.notEqual(a.bar, extended.bar) + assert.notEqual(b.bar, extended.bar) + assert.notEqual(a.mumble, extended.mumble) + assert.notEqual(a.mumble.qux, extended.mumble.qux) + }) + + it("should handle mismatch in type", () => { + const a = { foo: { bar: 0, baz: 2, qux: { mumble: 11 } }, garply: 4, waldo: { thud: 10 } } + const b = { foo: { bar: [1], baz: { plugh: 8 }, qux: 12 }, garply: { nox: 9 }, waldo: 7 } + const extended = extend(a, b) + assert.deepEqual(extended, { + foo: { bar: [1], baz: { plugh: 8 }, qux: 12 }, + garply: { nox: 9 }, + waldo: 7, + }) + }) + }) + + describe("normalize", () => { + it("should remove multiple slashes", () => { + assert.equal(normalize("//foo//bar//baz///mumble"), "/foo/bar/baz/mumble") + }) + + it("should remove trailing slashes", () => { + assert.equal(normalize("qux///"), "qux") + }) + + it("should preserve trailing slash if it exists", () => { + assert.equal(normalize("qux///", true), "qux/") + assert.equal(normalize("qux", true), "qux") + }) + }) +}) diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..2c7db3ea3 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "moduleResolution": "node", + "strict": true, + "noImplicitReturns": true, + "noUnusedLocals": true, + "forceConsistentCasingInFileNames": true, + "outDir": "./out", + "allowJs": false, + "jsx": "react", + "declaration": true, + "experimentalDecorators": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "sourceMap": true, + "tsBuildInfoFile": "./.tsbuildinfo", + "incremental": true, + "rootDir": "./src", + "typeRoots": ["./node_modules/@types", "./typings"] + }, + "include": ["./src/**/*.ts", "./src/**/*.tsx"] +} diff --git a/tslint.json b/tslint.json deleted file mode 100644 index 9c957f3ef..000000000 --- a/tslint.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": [ - "../../../tslint.json" - ], - "rules": { - "no-unexternalized-strings": false - } -} diff --git a/typings/.npmignore b/typings/.npmignore deleted file mode 100644 index 228e70f90..000000000 --- a/typings/.npmignore +++ /dev/null @@ -1 +0,0 @@ -httpolyglot.d.ts \ No newline at end of file diff --git a/typings/api.d.ts b/typings/api.d.ts deleted file mode 100644 index 6657f6eb5..000000000 --- a/typings/api.d.ts +++ /dev/null @@ -1,52 +0,0 @@ -import * as vscode from "vscode"; - -// Only export the subset of VS Code we have implemented. -export interface VSCodeApi { - EventEmitter: typeof vscode.EventEmitter; - FileSystemError: typeof vscode.FileSystemError; - FileType: typeof vscode.FileType; - StatusBarAlignment: typeof vscode.StatusBarAlignment; - ThemeColor: typeof vscode.ThemeColor; - TreeItemCollapsibleState: typeof vscode.TreeItemCollapsibleState; - Uri: typeof vscode.Uri; - commands: { - executeCommand: typeof vscode.commands.executeCommand; - registerCommand: typeof vscode.commands.registerCommand; - }; - window: { - createStatusBarItem: typeof vscode.window.createStatusBarItem; - registerTreeDataProvider: typeof vscode.window.registerTreeDataProvider; - showErrorMessage: typeof vscode.window.showErrorMessage; - }; - workspace: { - registerFileSystemProvider: typeof vscode.workspace.registerFileSystemProvider; - }; -} - -export interface CoderApi { - registerView: (viewId: string, viewName: string, containerId: string, containerName: string, icon: string) => void; -} - -export interface IdeReadyEvent extends CustomEvent { - readonly vscode: VSCodeApi; - readonly ide: CoderApi; -} - -declare global { - interface Window { - /** - * Full VS Code extension API. - */ - vscode?: VSCodeApi; - - /** - * Coder API. - */ - ide?: CoderApi; - - /** - * Listen for when the IDE API has been set and is ready to use. - */ - addEventListener(event: "ide-ready", callback: (event: IdeReadyEvent) => void): void; - } -} diff --git a/typings/httpolyglot.d.ts b/typings/httpolyglot.d.ts deleted file mode 100644 index aeb2fc05a..000000000 --- a/typings/httpolyglot.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -declare module "httpolyglot" { - import * as http from "http"; - import * as https from "https"; - - function createServer(requestListener?: (req: http.IncomingMessage, res: http.ServerResponse) => void): http.Server; - function createServer(options: https.ServerOptions, requestListener?: (req: http.IncomingMessage, res: http.ServerResponse) => void): https.Server; -} diff --git a/typings/httpolyglot/index.d.ts b/typings/httpolyglot/index.d.ts new file mode 100644 index 000000000..176e038ce --- /dev/null +++ b/typings/httpolyglot/index.d.ts @@ -0,0 +1,10 @@ +declare module "httpolyglot" { + import * as http from "http" + import * as https from "https" + + function createServer(requestListener?: (req: http.IncomingMessage, res: http.ServerResponse) => void): http.Server + function createServer( + options: https.ServerOptions, + requestListener?: (req: http.IncomingMessage, res: http.ServerResponse) => void, + ): https.Server +} diff --git a/typings/package.json b/typings/package.json deleted file mode 100644 index 11c1652e4..000000000 --- a/typings/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "@coder/ide-api", - "version": "2.0.3", - "typings": "api.d.ts", - "license": "MIT", - "author": "Coder", - "description": "API for interfacing with the API created for content-scripts.", - "dependencies": { - "@types/vscode": "^1.37.0" - } -} diff --git a/yarn.lock b/yarn.lock index a13a4ad72..df6d56719 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,34 +2,867 @@ # yarn lockfile v1 -"@coder/logger@^1.1.12", "@coder/logger@^1.1.8": - version "1.1.12" - resolved "https://registry.yarnpkg.com/@coder/logger/-/logger-1.1.12.tgz#def113b7183abc35a8da2b57f0929f7e9626f4e0" - integrity sha512-oM0j3lTVPqApUm3e0bKKcXpfAiJEys31fgEfQlHmvEA13ujsC4zDuXnt0uzDtph48eMoNRLOF/EE4mNShVJKVw== - -"@coder/nbin@^1.2.7": - version "1.2.7" - resolved "https://registry.yarnpkg.com/@coder/nbin/-/nbin-1.2.7.tgz#1c3c19480eba0fc3f6dd12a691e2f6041ba3c1b1" - integrity sha512-1t+8oXufU7/jNyXE3+aRlB1z3d+Foo/HQ0y0Bj0zmnhnlpZabEduFLVzEgbRrnzOwW3VtI5d2V1PbPp/XCen+A== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" + integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== dependencies: - "@coder/logger" "^1.1.8" - browserify "^16.5.0" - fs-extra "^7.0.1" - glob "^7.1.3" - node-fetch "^2.3.0" - ora "^3.2.0" + "@babel/highlight" "^7.8.3" -"@coder/node-browser@^1.0.6": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@coder/node-browser/-/node-browser-1.0.8.tgz#c22f581b089ad7d95ad1362fd351c57b7fbc6e70" - integrity sha512-NLF9sYMRCN9WK1C224pHax1Cay3qKypg25BhVg7VfNbo3Cpa3daata8RF/rT8JK3lPsu8PmFgDRQjzGC9X1Lrw== +"@babel/compat-data@^7.8.4": + version "7.8.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.8.5.tgz#d28ce872778c23551cbb9432fc68d28495b613b9" + integrity sha512-jWYUqQX/ObOhG1UiEkbH5SANsE/8oKXiQWjj7p7xgj9Zmnt//aUvyz4dBkK0HNsS8/cbyC5NmmH87VekW+mXFg== + dependencies: + browserslist "^4.8.5" + invariant "^2.2.4" + semver "^5.5.0" -"@coder/requirefs@^1.0.6": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@coder/requirefs/-/requirefs-1.1.2.tgz#20b0be27dde1b714853afc67b031a353d11988d7" - integrity sha512-dXslHsPr6PkgtiFV1oUr6XwgCBia4Ct3fmJJCJbzGn7bDNToy5RzB84pZ6we/yj5w2z1awzpQdKusX52OKh3VA== - optionalDependencies: - jszip "2.6.0" +"@babel/core@>=7.2.2", "@babel/core@^7.4.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.8.4.tgz#d496799e5c12195b3602d0fddd77294e3e38e80e" + integrity sha512-0LiLrB2PwrVI+a2/IEskBopDYSd8BCb3rOvH7D5tzoWd696TBEduBvuLVm4Nx6rltrLZqvI3MCalB2K2aVzQjA== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/generator" "^7.8.4" + "@babel/helpers" "^7.8.4" + "@babel/parser" "^7.8.4" + "@babel/template" "^7.8.3" + "@babel/traverse" "^7.8.4" + "@babel/types" "^7.8.3" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.0" + lodash "^4.17.13" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/generator@^7.4.4", "@babel/generator@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.4.tgz#35bbc74486956fe4251829f9f6c48330e8d0985e" + integrity sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA== + dependencies: + "@babel/types" "^7.8.3" + jsesc "^2.5.1" + lodash "^4.17.13" + source-map "^0.5.0" + +"@babel/helper-annotate-as-pure@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz#60bc0bc657f63a0924ff9a4b4a0b24a13cf4deee" + integrity sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz#c84097a427a061ac56a1c30ebf54b7b22d241503" + integrity sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw== + dependencies: + "@babel/helper-explode-assignable-expression" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-builder-react-jsx@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.8.3.tgz#dee98d7d79cc1f003d80b76fe01c7f8945665ff6" + integrity sha512-JT8mfnpTkKNCboTqZsQTdGo3l3Ik3l7QIt9hh0O9DYiwVel37VoJpILKM4YFbP2euF32nkQSb+F9cUk9b7DDXQ== + dependencies: + "@babel/types" "^7.8.3" + esutils "^2.0.0" + +"@babel/helper-call-delegate@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.8.3.tgz#de82619898aa605d409c42be6ffb8d7204579692" + integrity sha512-6Q05px0Eb+N4/GTyKPPvnkig7Lylw+QzihMpws9iiZQv7ZImf84ZsZpQH7QoWN4n4tm81SnSzPgHw2qtO0Zf3A== + dependencies: + "@babel/helper-hoist-variables" "^7.8.3" + "@babel/traverse" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-compilation-targets@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.4.tgz#03d7ecd454b7ebe19a254f76617e61770aed2c88" + integrity sha512-3k3BsKMvPp5bjxgMdrFyq0UaEO48HciVrOVF0+lon8pp95cyJ2ujAh0TrBHNMnJGT2rr0iKOJPFFbSqjDyf/Pg== + dependencies: + "@babel/compat-data" "^7.8.4" + browserslist "^4.8.5" + invariant "^2.2.4" + levenary "^1.1.1" + semver "^5.5.0" + +"@babel/helper-create-regexp-features-plugin@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.3.tgz#c774268c95ec07ee92476a3862b75cc2839beb79" + integrity sha512-Gcsm1OHCUr9o9TcJln57xhWHtdXbA2pgQ58S0Lxlks0WMGNXuki4+GLfX0p+L2ZkINUGZvfkz8rzoqJQSthI+Q== + dependencies: + "@babel/helper-regex" "^7.8.3" + regexpu-core "^4.6.0" + +"@babel/helper-define-map@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz#a0655cad5451c3760b726eba875f1cd8faa02c15" + integrity sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g== + dependencies: + "@babel/helper-function-name" "^7.8.3" + "@babel/types" "^7.8.3" + lodash "^4.17.13" + +"@babel/helper-explode-assignable-expression@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz#a728dc5b4e89e30fc2dfc7d04fa28a930653f982" + integrity sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw== + dependencies: + "@babel/traverse" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-function-name@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz#eeeb665a01b1f11068e9fb86ad56a1cb1a824cca" + integrity sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA== + dependencies: + "@babel/helper-get-function-arity" "^7.8.3" + "@babel/template" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-get-function-arity@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5" + integrity sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-hoist-variables@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz#1dbe9b6b55d78c9b4183fc8cdc6e30ceb83b7134" + integrity sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-member-expression-to-functions@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz#659b710498ea6c1d9907e0c73f206eee7dadc24c" + integrity sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-module-imports@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz#7fe39589b39c016331b6b8c3f441e8f0b1419498" + integrity sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-module-transforms@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.8.3.tgz#d305e35d02bee720fbc2c3c3623aa0c316c01590" + integrity sha512-C7NG6B7vfBa/pwCOshpMbOYUmrYQDfCpVL/JCRu0ek8B5p8kue1+BCXpg2vOYs7w5ACB9GTOBYQ5U6NwrMg+3Q== + dependencies: + "@babel/helper-module-imports" "^7.8.3" + "@babel/helper-simple-access" "^7.8.3" + "@babel/helper-split-export-declaration" "^7.8.3" + "@babel/template" "^7.8.3" + "@babel/types" "^7.8.3" + lodash "^4.17.13" + +"@babel/helper-optimise-call-expression@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz#7ed071813d09c75298ef4f208956006b6111ecb9" + integrity sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670" + integrity sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ== + +"@babel/helper-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.8.3.tgz#139772607d51b93f23effe72105b319d2a4c6965" + integrity sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ== + dependencies: + lodash "^4.17.13" + +"@babel/helper-remap-async-to-generator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz#273c600d8b9bf5006142c1e35887d555c12edd86" + integrity sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.8.3" + "@babel/helper-wrap-function" "^7.8.3" + "@babel/template" "^7.8.3" + "@babel/traverse" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-replace-supers@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.8.3.tgz#91192d25f6abbcd41da8a989d4492574fb1530bc" + integrity sha512-xOUssL6ho41U81etpLoT2RTdvdus4VfHamCuAm4AHxGr+0it5fnwoVdwUJ7GFEqCsQYzJUhcbsN9wB9apcYKFA== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.8.3" + "@babel/helper-optimise-call-expression" "^7.8.3" + "@babel/traverse" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-simple-access@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz#7f8109928b4dab4654076986af575231deb639ae" + integrity sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw== + dependencies: + "@babel/template" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-split-export-declaration@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9" + integrity sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-wrap-function@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz#9dbdb2bb55ef14aaa01fe8c99b629bd5352d8610" + integrity sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ== + dependencies: + "@babel/helper-function-name" "^7.8.3" + "@babel/template" "^7.8.3" + "@babel/traverse" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helpers@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.8.4.tgz#754eb3ee727c165e0a240d6c207de7c455f36f73" + integrity sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w== + dependencies: + "@babel/template" "^7.8.3" + "@babel/traverse" "^7.8.4" + "@babel/types" "^7.8.3" + +"@babel/highlight@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797" + integrity sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg== + dependencies: + chalk "^2.0.0" + esutils "^2.0.2" + js-tokens "^4.0.0" + +"@babel/parser@^7.4.4", "@babel/parser@^7.8.3", "@babel/parser@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.4.tgz#d1dbe64691d60358a974295fa53da074dd2ce8e8" + integrity sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw== + +"@babel/plugin-proposal-async-generator-functions@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz#bad329c670b382589721b27540c7d288601c6e6f" + integrity sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-remap-async-to-generator" "^7.8.3" + "@babel/plugin-syntax-async-generators" "^7.8.0" + +"@babel/plugin-proposal-dynamic-import@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz#38c4fe555744826e97e2ae930b0fb4cc07e66054" + integrity sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + +"@babel/plugin-proposal-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz#da5216b238a98b58a1e05d6852104b10f9a70d6b" + integrity sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.0" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz#e4572253fdeed65cddeecfdab3f928afeb2fd5d2" + integrity sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + +"@babel/plugin-proposal-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.8.3.tgz#eb5ae366118ddca67bed583b53d7554cad9951bb" + integrity sha512-8qvuPwU/xxUCt78HocNlv0mXXo0wdh9VT1R04WU8HGOfaOob26pF+9P5/lYjN/q7DHOX1bvX60hnhOvuQUJdbA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + +"@babel/plugin-proposal-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz#9dee96ab1650eed88646ae9734ca167ac4a9c5c9" + integrity sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + +"@babel/plugin-proposal-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.8.3.tgz#ae10b3214cb25f7adb1f3bc87ba42ca10b7e2543" + integrity sha512-QIoIR9abkVn+seDE3OjA08jWcs3eZ9+wJCKSRgo3WdEU2csFYgdScb+8qHB3+WXsGJD55u+5hWCISI7ejXS+kg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + +"@babel/plugin-proposal-unicode-property-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.3.tgz#b646c3adea5f98800c9ab45105ac34d06cd4a47f" + integrity sha512-1/1/rEZv2XGweRwwSkLpY+s60za9OZ1hJs4YDqFHCw0kYWYwL5IFljVY1MYBL+weT1l9pokDO2uhSTLVxzoHkQ== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-async-generators@^7.8.0": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-dynamic-import@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-flow@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.8.3.tgz#f2c883bd61a6316f2c89380ae5122f923ba4527f" + integrity sha512-innAx3bUbA0KSYj2E2MNFSn9hiCeowOFLxlsuhXzw8hMQnzkDomUr9QCD7E9VF60NmnG1sNTuuv6Qf4f8INYsg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-json-strings@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-jsx@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.8.3.tgz#521b06c83c40480f1e58b4fd33b92eceb1d6ea94" + integrity sha512-WxdW9xyLgBdefoo0Ynn3MRSkhe5tFVxxKNVdnZSh318WrG2e2jH+E9wd/++JsqcLJZPfz87njQJ8j2Upjm0M0A== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-object-rest-spread@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-top-level-await@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz#3acdece695e6b13aaf57fc291d1a800950c71391" + integrity sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-arrow-functions@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz#82776c2ed0cd9e1a49956daeb896024c9473b8b6" + integrity sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-async-to-generator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz#4308fad0d9409d71eafb9b1a6ee35f9d64b64086" + integrity sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ== + dependencies: + "@babel/helper-module-imports" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-remap-async-to-generator" "^7.8.3" + +"@babel/plugin-transform-block-scoped-functions@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz#437eec5b799b5852072084b3ae5ef66e8349e8a3" + integrity sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-block-scoping@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz#97d35dab66857a437c166358b91d09050c868f3a" + integrity sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + lodash "^4.17.13" + +"@babel/plugin-transform-classes@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.8.3.tgz#46fd7a9d2bb9ea89ce88720477979fe0d71b21b8" + integrity sha512-SjT0cwFJ+7Rbr1vQsvphAHwUHvSUPmMjMU/0P59G8U2HLFqSa082JO7zkbDNWs9kH/IUqpHI6xWNesGf8haF1w== + dependencies: + "@babel/helper-annotate-as-pure" "^7.8.3" + "@babel/helper-define-map" "^7.8.3" + "@babel/helper-function-name" "^7.8.3" + "@babel/helper-optimise-call-expression" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-replace-supers" "^7.8.3" + "@babel/helper-split-export-declaration" "^7.8.3" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz#96d0d28b7f7ce4eb5b120bb2e0e943343c86f81b" + integrity sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-destructuring@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.8.3.tgz#20ddfbd9e4676906b1056ee60af88590cc7aaa0b" + integrity sha512-H4X646nCkiEcHZUZaRkhE2XVsoz0J/1x3VVujnn96pSoGCtKPA99ZZA+va+gK+92Zycd6OBKCD8tDb/731bhgQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-dotall-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz#c3c6ec5ee6125c6993c5cbca20dc8621a9ea7a6e" + integrity sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-duplicate-keys@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz#8d12df309aa537f272899c565ea1768e286e21f1" + integrity sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-exponentiation-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz#581a6d7f56970e06bf51560cd64f5e947b70d7b7" + integrity sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-flow-strip-types@^7.4.4": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.8.3.tgz#da705a655466b2a9b36046b57bf0cbcd53551bd4" + integrity sha512-g/6WTWG/xbdd2exBBzMfygjX/zw4eyNC4X8pRaq7aRHRoDUCzAIu3kGYIXviOv8BjCuWm8vDBwjHcjiRNgXrPA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-flow" "^7.8.3" + +"@babel/plugin-transform-for-of@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.8.4.tgz#6fe8eae5d6875086ee185dd0b098a8513783b47d" + integrity sha512-iAXNlOWvcYUYoV8YIxwS7TxGRJcxyl8eQCfT+A5j8sKUzRFvJdcyjp97jL2IghWSRDaL2PU2O2tX8Cu9dTBq5A== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-function-name@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz#279373cb27322aaad67c2683e776dfc47196ed8b" + integrity sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ== + dependencies: + "@babel/helper-function-name" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-literals@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz#aef239823d91994ec7b68e55193525d76dbd5dc1" + integrity sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-member-expression-literals@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz#963fed4b620ac7cbf6029c755424029fa3a40410" + integrity sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-modules-amd@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.8.3.tgz#65606d44616b50225e76f5578f33c568a0b876a5" + integrity sha512-MadJiU3rLKclzT5kBH4yxdry96odTUwuqrZM+GllFI/VhxfPz+k9MshJM+MwhfkCdxxclSbSBbUGciBngR+kEQ== + dependencies: + "@babel/helper-module-transforms" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + babel-plugin-dynamic-import-node "^2.3.0" + +"@babel/plugin-transform-modules-commonjs@^7.4.4", "@babel/plugin-transform-modules-commonjs@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.8.3.tgz#df251706ec331bd058a34bdd72613915f82928a5" + integrity sha512-JpdMEfA15HZ/1gNuB9XEDlZM1h/gF/YOH7zaZzQu2xCFRfwc01NXBMHHSTT6hRjlXJJs5x/bfODM3LiCk94Sxg== + dependencies: + "@babel/helper-module-transforms" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-simple-access" "^7.8.3" + babel-plugin-dynamic-import-node "^2.3.0" + +"@babel/plugin-transform-modules-systemjs@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.8.3.tgz#d8bbf222c1dbe3661f440f2f00c16e9bb7d0d420" + integrity sha512-8cESMCJjmArMYqa9AO5YuMEkE4ds28tMpZcGZB/jl3n0ZzlsxOAi3mC+SKypTfT8gjMupCnd3YiXCkMjj2jfOg== + dependencies: + "@babel/helper-hoist-variables" "^7.8.3" + "@babel/helper-module-transforms" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + babel-plugin-dynamic-import-node "^2.3.0" + +"@babel/plugin-transform-modules-umd@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.8.3.tgz#592d578ce06c52f5b98b02f913d653ffe972661a" + integrity sha512-evhTyWhbwbI3/U6dZAnx/ePoV7H6OUG+OjiJFHmhr9FPn0VShjwC2kdxqIuQ/+1P50TMrneGzMeyMTFOjKSnAw== + dependencies: + "@babel/helper-module-transforms" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz#a2a72bffa202ac0e2d0506afd0939c5ecbc48c6c" + integrity sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.8.3" + +"@babel/plugin-transform-new-target@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz#60cc2ae66d85c95ab540eb34babb6434d4c70c43" + integrity sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-object-super@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz#ebb6a1e7a86ffa96858bd6ac0102d65944261725" + integrity sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-replace-supers" "^7.8.3" + +"@babel/plugin-transform-parameters@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.8.4.tgz#1d5155de0b65db0ccf9971165745d3bb990d77d3" + integrity sha512-IsS3oTxeTsZlE5KqzTbcC2sV0P9pXdec53SU+Yxv7o/6dvGM5AkTotQKhoSffhNgZ/dftsSiOoxy7evCYJXzVA== + dependencies: + "@babel/helper-call-delegate" "^7.8.3" + "@babel/helper-get-function-arity" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-property-literals@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz#33194300d8539c1ed28c62ad5087ba3807b98263" + integrity sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-react-jsx@^7.0.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.8.3.tgz#4220349c0390fdefa505365f68c103562ab2fc4a" + integrity sha512-r0h+mUiyL595ikykci+fbwm9YzmuOrUBi0b+FDIKmi3fPQyFokWVEMJnRWHJPPQEjyFJyna9WZC6Viv6UHSv1g== + dependencies: + "@babel/helper-builder-react-jsx" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-jsx" "^7.8.3" + +"@babel/plugin-transform-regenerator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.3.tgz#b31031e8059c07495bf23614c97f3d9698bc6ec8" + integrity sha512-qt/kcur/FxrQrzFR432FGZznkVAjiyFtCOANjkAKwCbt465L6ZCiUQh2oMYGU3Wo8LRFJxNDFwWn106S5wVUNA== + dependencies: + regenerator-transform "^0.14.0" + +"@babel/plugin-transform-reserved-words@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz#9a0635ac4e665d29b162837dd3cc50745dfdf1f5" + integrity sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-shorthand-properties@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz#28545216e023a832d4d3a1185ed492bcfeac08c8" + integrity sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz#9c8ffe8170fdfb88b114ecb920b82fb6e95fe5e8" + integrity sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-sticky-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz#be7a1290f81dae767475452199e1f76d6175b100" + integrity sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-regex" "^7.8.3" + +"@babel/plugin-transform-template-literals@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz#7bfa4732b455ea6a43130adc0ba767ec0e402a80" + integrity sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-typeof-symbol@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz#ede4062315ce0aaf8a657a920858f1a2f35fc412" + integrity sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-unicode-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz#0cef36e3ba73e5c57273effb182f46b91a1ecaad" + integrity sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/preset-env@^7.4.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.8.4.tgz#9dac6df5f423015d3d49b6e9e5fa3413e4a72c4e" + integrity sha512-HihCgpr45AnSOHRbS5cWNTINs0TwaR8BS8xIIH+QwiW8cKL0llV91njQMpeMReEPVs+1Ao0x3RLEBLtt1hOq4w== + dependencies: + "@babel/compat-data" "^7.8.4" + "@babel/helper-compilation-targets" "^7.8.4" + "@babel/helper-module-imports" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-proposal-async-generator-functions" "^7.8.3" + "@babel/plugin-proposal-dynamic-import" "^7.8.3" + "@babel/plugin-proposal-json-strings" "^7.8.3" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-proposal-object-rest-spread" "^7.8.3" + "@babel/plugin-proposal-optional-catch-binding" "^7.8.3" + "@babel/plugin-proposal-optional-chaining" "^7.8.3" + "@babel/plugin-proposal-unicode-property-regex" "^7.8.3" + "@babel/plugin-syntax-async-generators" "^7.8.0" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/plugin-syntax-json-strings" "^7.8.0" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + "@babel/plugin-syntax-top-level-await" "^7.8.3" + "@babel/plugin-transform-arrow-functions" "^7.8.3" + "@babel/plugin-transform-async-to-generator" "^7.8.3" + "@babel/plugin-transform-block-scoped-functions" "^7.8.3" + "@babel/plugin-transform-block-scoping" "^7.8.3" + "@babel/plugin-transform-classes" "^7.8.3" + "@babel/plugin-transform-computed-properties" "^7.8.3" + "@babel/plugin-transform-destructuring" "^7.8.3" + "@babel/plugin-transform-dotall-regex" "^7.8.3" + "@babel/plugin-transform-duplicate-keys" "^7.8.3" + "@babel/plugin-transform-exponentiation-operator" "^7.8.3" + "@babel/plugin-transform-for-of" "^7.8.4" + "@babel/plugin-transform-function-name" "^7.8.3" + "@babel/plugin-transform-literals" "^7.8.3" + "@babel/plugin-transform-member-expression-literals" "^7.8.3" + "@babel/plugin-transform-modules-amd" "^7.8.3" + "@babel/plugin-transform-modules-commonjs" "^7.8.3" + "@babel/plugin-transform-modules-systemjs" "^7.8.3" + "@babel/plugin-transform-modules-umd" "^7.8.3" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.8.3" + "@babel/plugin-transform-new-target" "^7.8.3" + "@babel/plugin-transform-object-super" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.8.4" + "@babel/plugin-transform-property-literals" "^7.8.3" + "@babel/plugin-transform-regenerator" "^7.8.3" + "@babel/plugin-transform-reserved-words" "^7.8.3" + "@babel/plugin-transform-shorthand-properties" "^7.8.3" + "@babel/plugin-transform-spread" "^7.8.3" + "@babel/plugin-transform-sticky-regex" "^7.8.3" + "@babel/plugin-transform-template-literals" "^7.8.3" + "@babel/plugin-transform-typeof-symbol" "^7.8.4" + "@babel/plugin-transform-unicode-regex" "^7.8.3" + "@babel/types" "^7.8.3" + browserslist "^4.8.5" + core-js-compat "^3.6.2" + invariant "^2.2.2" + levenary "^1.1.1" + semver "^5.5.0" + +"@babel/runtime@^7.4.4", "@babel/runtime@^7.6.3": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.4.tgz#d79f5a2040f7caa24d53e563aad49cbc05581308" + integrity sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ== + dependencies: + regenerator-runtime "^0.13.2" + +"@babel/template@^7.4.4", "@babel/template@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.3.tgz#e02ad04fe262a657809327f578056ca15fd4d1b8" + integrity sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/parser" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/traverse@^7.4.4", "@babel/traverse@^7.8.3", "@babel/traverse@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.4.tgz#f0845822365f9d5b0e312ed3959d3f827f869e3c" + integrity sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/generator" "^7.8.4" + "@babel/helper-function-name" "^7.8.3" + "@babel/helper-split-export-declaration" "^7.8.3" + "@babel/parser" "^7.8.4" + "@babel/types" "^7.8.3" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.13" + +"@babel/types@^7.4.4", "@babel/types@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.3.tgz#5a383dffa5416db1b73dedffd311ffd0788fb31c" + integrity sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg== + dependencies: + esutils "^2.0.2" + lodash "^4.17.13" + to-fast-properties "^2.0.0" + +"@coder/logger@1.1.11": + version "1.1.11" + resolved "https://registry.yarnpkg.com/@coder/logger/-/logger-1.1.11.tgz#e6f36dba9436ae61e66e3f66787d75c768617605" + integrity sha512-EEh1dqSU0AaqjjjMsVqumgZGbrZimKFKIb4t5E6o3FLfVUxJCReSME78Yj2N1xWUVAHMnqafDCxLostpuIotzw== + +"@iarna/toml@^2.2.0": + version "2.2.3" + resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.3.tgz#f060bf6eaafae4d56a7dac618980838b0696e2ab" + integrity sha512-FmuxfCuolpLl0AnQ2NHSzoUKWEJDFl63qXjzdoWBVyFCXzMGm1spBzk7LeHNoVCiWCF7mRVms9e6jEV9+MoPbg== + +"@mrmlnc/readdir-enhanced@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" + integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g== + dependencies: + call-me-maybe "^1.0.1" + glob-to-regexp "^0.3.0" + +"@nodelib/fs.scandir@2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b" + integrity sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw== + dependencies: + "@nodelib/fs.stat" "2.0.3" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.3", "@nodelib/fs.stat@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3" + integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA== + +"@nodelib/fs.stat@^1.1.2": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" + integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976" + integrity sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ== + dependencies: + "@nodelib/fs.scandir" "2.1.3" + fastq "^1.6.0" + +"@parcel/fs@^1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@parcel/fs/-/fs-1.11.0.tgz#fb8a2be038c454ad46a50dc0554c1805f13535cd" + integrity sha512-86RyEqULbbVoeo8OLcv+LQ1Vq2PKBAvWTU9fCgALxuCTbbs5Ppcvll4Vr+Ko1AnmMzja/k++SzNAwJfeQXVlpA== + dependencies: + "@parcel/utils" "^1.11.0" + mkdirp "^0.5.1" + rimraf "^2.6.2" + +"@parcel/logger@^1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@parcel/logger/-/logger-1.11.1.tgz#c55b0744bcbe84ebc291155627f0ec406a23e2e6" + integrity sha512-9NF3M6UVeP2udOBDILuoEHd8VrF4vQqoWHEafymO1pfSoOMfxrSJZw1MfyAAIUN/IFp9qjcpDCUbDZB+ioVevA== + dependencies: + "@parcel/workers" "^1.11.0" + chalk "^2.1.0" + grapheme-breaker "^0.3.2" + ora "^2.1.0" + strip-ansi "^4.0.0" + +"@parcel/utils@^1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@parcel/utils/-/utils-1.11.0.tgz#539e08fff8af3b26eca11302be80b522674b51ea" + integrity sha512-cA3p4jTlaMeOtAKR/6AadanOPvKeg8VwgnHhOyfi0yClD0TZS/hi9xu12w4EzA/8NtHu0g6o4RDfcNjqN8l1AQ== + +"@parcel/watcher@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-1.12.1.tgz#b98b3df309fcab93451b5583fc38e40826696dad" + integrity sha512-od+uCtCxC/KoNQAIE1vWx1YTyKYY+7CTrxBJPRh3cDWw/C0tCtlBMVlrbplscGoEpt6B27KhJDCv82PBxOERNA== + dependencies: + "@parcel/utils" "^1.11.0" + chokidar "^2.1.5" + +"@parcel/workers@^1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@parcel/workers/-/workers-1.11.0.tgz#7b8dcf992806f4ad2b6cecf629839c41c2336c59" + integrity sha512-USSjRAAQYsZFlv43FUPdD+jEGML5/8oLF0rUzPQTtK4q9kvaXr49F5ZplyLz5lox78cLZ0TxN2bIDQ1xhOkulQ== + dependencies: + "@parcel/utils" "^1.11.0" + physical-cpu-count "^2.0.0" + +"@types/adm-zip@^0.4.32": + version "0.4.32" + resolved "https://registry.yarnpkg.com/@types/adm-zip/-/adm-zip-0.4.32.tgz#6de01309af60677065d2e52b417a023303220931" + integrity sha512-hv1O7ySn+XvP5OeDQcJFWwVb2v+GFGO1A9aMTQ5B/bzxb7WW21O8iRhVdsKKr8QwuiagzGmPP+gsUAYZ6bRddQ== + dependencies: + "@types/node" "*" + +"@types/color-name@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" + integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== + +"@types/eslint-visitor-keys@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" + integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== + +"@types/express-serve-static-core@*": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.2.tgz#f6f41fa35d42e79dbf6610eccbb2637e6008a0cf" + integrity sha512-El9yMpctM6tORDAiBwZVLMcxoTMcqqRO9dVyYcn7ycLWbvR8klrDn8CAOwRfZujZtWD7yS/mshTdz43jMOejbg== + dependencies: + "@types/node" "*" + "@types/range-parser" "*" "@types/fs-extra@^8.0.1": version "8.0.1" @@ -38,10 +871,42 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@^10.12.12": - version "10.17.13" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.13.tgz#ccebcdb990bd6139cd16e84c39dc2fb1023ca90c" - integrity sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg== +"@types/json-schema@^7.0.3": + version "7.0.4" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" + integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== + +"@types/minimist@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.0.tgz#69a23a3ad29caf0097f06eda59b361ee2f0639f6" + integrity sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY= + +"@types/mocha@^5.2.7": + version "5.2.7" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.7.tgz#315d570ccb56c53452ff8638738df60726d5b6ea" + integrity sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ== + +"@types/node@*", "@types/node@^12.12.7": + version "12.12.27" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.27.tgz#d7506f73160ad30fcebbcf5b8b7d2d976e649e42" + integrity sha512-odQFl/+B9idbdS0e8IxDl2ia/LP8KZLXhV3BUeI98TrZp0uoIzQPhGd+5EtzHmT0SMOIaPd7jfz6pOHLWTtl7A== + +"@types/normalize-package-data@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" + integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== + +"@types/parcel-bundler@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@types/parcel-bundler/-/parcel-bundler-1.12.1.tgz#21c16a4912393b2d6414d1f1f9e886a30f79f2e6" + integrity sha512-I7Cy3Uy/HqMa3E7dyPxTkiPFx0gi3bIQxAcZCP8v1vFx+esdVFgbZBnusa6VfoIeFk61/fnVl25RyYKgDHVLtw== + dependencies: + "@types/express-serve-static-core" "*" + +"@types/parse-json@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/pem@^1.9.5": version "1.9.5" @@ -50,12 +915,29 @@ dependencies: "@types/node" "*" +"@types/q@^1.5.1": + version "1.5.2" + resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8" + integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw== + +"@types/range-parser@*": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" + integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== + "@types/safe-compare@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@types/safe-compare/-/safe-compare-1.1.0.tgz#47ed9b9ca51a3a791b431cd59b28f47fa9bf1224" integrity sha512-1ri+LJhh0gRxIa37IpGytdaW7yDEHeJniBSMD1BmitS07R1j63brcYCzry+l0WJvGdEKQNQ7DYXO2epgborWPw== -"@types/tar-fs@^1.16.1": +"@types/semver@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.1.0.tgz#c8c630d4c18cd326beff77404887596f96408408" + integrity sha512-pOKLaubrAEMUItGNpgwl0HMFPrSAFic8oSVIvfu1UwcgGNmNyK9gyhBHKmBnUTwwVvpZfkzUC0GaMgnL6P86uA== + dependencies: + "@types/node" "*" + +"@types/tar-fs@^1.16.2": version "1.16.2" resolved "https://registry.yarnpkg.com/@types/tar-fs/-/tar-fs-1.16.2.tgz#6f5acea15d3b7777b8bf3f1c6d4e80ce71288f34" integrity sha512-eds/pbRf0Fe0EKmrHDbs8mRkfbjz2upAdoUfREw14dPboZaHqqZ1Y+uVeoakoPavpZMpj22nhUTAYkX5bz3DXA== @@ -63,51 +945,158 @@ "@types/node" "*" "@types/tar-stream" "*" -"@types/tar-stream@*", "@types/tar-stream@^1.6.1": +"@types/tar-stream@*": version "1.6.1" resolved "https://registry.yarnpkg.com/@types/tar-stream/-/tar-stream-1.6.1.tgz#67d759068ff781d976cad978893bb7a334ec8809" integrity sha512-pYCDOPuRE+4tXFk1rSMYiuI+kSrXiJ4av1bboQbkcEBA2rqwEWfIn9kdMSH+5nYu58WksHuxwx+7kVbtg0Le7w== dependencies: "@types/node" "*" -JSONStream@^1.0.3: - version "1.3.5" - resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" - integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== +"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e" + integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ== + +"@types/vfile-message@*": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/vfile-message/-/vfile-message-2.0.0.tgz#690e46af0fdfc1f9faae00cd049cc888957927d5" + integrity sha512-GpTIuDpb9u4zIO165fUy9+fXcULdD8HFRNli04GehoMVbeNq7D6OBnqSmg3lxZnC+UvgUhEWKxdKiwYUkGltIw== dependencies: - jsonparse "^1.2.0" - through ">=2.2.7 <3" + vfile-message "*" -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - -acorn-node@^1.2.0, acorn-node@^1.3.0, acorn-node@^1.5.2, acorn-node@^1.6.1: - version "1.8.2" - resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8" - integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A== +"@types/vfile@^3.0.0": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/vfile/-/vfile-3.0.2.tgz#19c18cd232df11ce6fa6ad80259bc86c366b09b9" + integrity sha512-b3nLFGaGkJ9rzOcuXRfHkZMdjsawuDD0ENL9fzTophtBg8FJHSGbH7daXkEpcwy3v7Xol3pAvsmlYyFhR4pqJw== dependencies: - acorn "^7.0.0" - acorn-walk "^7.0.0" - xtend "^4.0.2" + "@types/node" "*" + "@types/unist" "*" + "@types/vfile-message" "*" -acorn-walk@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.0.0.tgz#c8ba6f0f1aac4b0a9e32d1f0af12be769528f36b" - integrity sha512-7Bv1We7ZGuU79zZbb6rRqcpxo3OY+zrdtloZWoyD8fmGX+FeXRjE+iuGkZjSXLVovLzrsvMGMy0EkwA0E0umxg== +"@types/ws@^6.0.4": + version "6.0.4" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-6.0.4.tgz#7797707c8acce8f76d8c34b370d4645b70421ff1" + integrity sha512-PpPrX7SZW9re6+Ha8ojZG4Se8AZXgf0GK6zmfqEuCsY49LFDNXO3SByp44X3dFEqtB73lkCDAdUazhAjVPiNwg== + dependencies: + "@types/node" "*" -acorn@^7.0.0: +"@typescript-eslint/eslint-plugin@^2.0.0": + version "2.19.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.19.2.tgz#e279aaae5d5c1f2547b4cff99204e1250bc7a058" + integrity sha512-HX2qOq2GOV04HNrmKnTpSIpHjfl7iwdXe3u/Nvt+/cpmdvzYvY0NHSiTkYN257jHnq4OM/yo+OsFgati+7LqJA== + dependencies: + "@typescript-eslint/experimental-utils" "2.19.2" + eslint-utils "^1.4.3" + functional-red-black-tree "^1.0.1" + regexpp "^3.0.0" + tsutils "^3.17.1" + +"@typescript-eslint/experimental-utils@2.19.2": + version "2.19.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.19.2.tgz#4611d44cf0f0cb460c26aa7676fc0a787281e233" + integrity sha512-B88QuwT1wMJR750YvTJBNjMZwmiPpbmKYLm1yI7PCc3x0NariqPwqaPsoJRwU9DmUi0cd9dkhz1IqEnwfD+P1A== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/typescript-estree" "2.19.2" + eslint-scope "^5.0.0" + +"@typescript-eslint/parser@^2.0.0": + version "2.19.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.19.2.tgz#21f42c0694846367e7d6a907feb08ab2f89c0879" + integrity sha512-8uwnYGKqX9wWHGPGdLB9sk9+12sjcdqEEYKGgbS8A0IvYX59h01o8os5qXUHMq2na8vpDRaV0suTLM7S8wraTA== + dependencies: + "@types/eslint-visitor-keys" "^1.0.0" + "@typescript-eslint/experimental-utils" "2.19.2" + "@typescript-eslint/typescript-estree" "2.19.2" + eslint-visitor-keys "^1.1.0" + +"@typescript-eslint/typescript-estree@2.19.2": + version "2.19.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.19.2.tgz#67485b00172f400474d243c6c0be27581a579350" + integrity sha512-Xu/qa0MDk6upQWqE4Qy2X16Xg8Vi32tQS2PR0AvnT/ZYS4YGDvtn2MStOh5y8Zy2mg4NuL06KUHlvCh95j9C6Q== + dependencies: + debug "^4.1.1" + eslint-visitor-keys "^1.1.0" + glob "^7.1.6" + is-glob "^4.0.1" + lodash "^4.17.15" + semver "^6.3.0" + tsutils "^3.17.1" + +abab@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.3.tgz#623e2075e02eb2d3f2475e49f99c91846467907a" + integrity sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg== + +acorn-globals@^4.3.0: + version "4.3.4" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" + integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A== + dependencies: + acorn "^6.0.1" + acorn-walk "^6.0.1" + +acorn-jsx@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.1.0.tgz#294adb71b57398b0680015f0a38c563ee1db5384" + integrity sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw== + +acorn-walk@^6.0.1: + version "6.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" + integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== + +acorn@^5.0.0: + version "5.7.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" + integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== + +acorn@^6.0.1, acorn@^6.0.4: + version "6.4.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.0.tgz#b659d2ffbafa24baf5db1cdbb2c94a983ecd2784" + integrity sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw== + +acorn@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c" integrity sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ== -ansi-align@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" - integrity sha1-w2rsy6VjuJzrVW82kPCx2eNUf38= +adm-zip@^0.4.14: + version "0.4.14" + resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.14.tgz#2cf312bcc9f8875df835b0f6040bd89be0a727a9" + integrity sha512-/9aQCnQHF+0IiCl0qhXoK7qs//SwYE7zX8lsr/DNk1BRAHYxeLZPL4pguwK29gUEqasYQjqPtEpDRSWEkdHn9g== + +ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5: + version "6.11.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.11.0.tgz#c3607cbc8ae392d8a5a536f25b21f8e5f3f87fe9" + integrity sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA== dependencies: - string-width "^2.0.0" + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +alphanum-sort@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" + integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= + +ansi-colors@3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" + integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== + +ansi-escapes@^4.2.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.0.tgz#a4ce2b33d6b214b7950d8595c212f12ac9cc569d" + integrity sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg== + dependencies: + type-fest "^0.8.1" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= ansi-regex@^3.0.0: version "3.0.0" @@ -119,13 +1108,38 @@ ansi-regex@^4.1.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== -ansi-styles@^3.2.1: +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" +ansi-styles@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" + integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== + dependencies: + "@types/color-name" "^1.1.1" + color-convert "^2.0.1" + +ansi-to-html@^0.6.4: + version "0.6.14" + resolved "https://registry.yarnpkg.com/ansi-to-html/-/ansi-to-html-0.6.14.tgz#65fe6d08bba5dd9db33f44a20aec331e0010dad8" + integrity sha512-7ZslfB1+EnFSDO5Ju+ue5Y6It19DRnZXWv8jrGHgIlPna5Mh4jz7BV5jCbQneXNFurQcKoolaaAjHtgSBfOIuA== + dependencies: + entities "^1.1.2" + anymatch@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" @@ -135,9 +1149,16 @@ anymatch@^2.0.0: normalize-path "^2.1.1" arg@^4.1.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.2.tgz#e70c90579e02c63d80e3ad4e31d8bfdb8bd50064" - integrity sha512-+ytCkGcBtHZ3V2r2Z06AncYO8jz46UEamcspGoU8lHcEbpn6J77QK0vdWvChsclg/tM5XIJC5tnjmPp7Eq6Obg== + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" arr-diff@^4.0.0: version "4.0.0" @@ -154,11 +1175,43 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= +array-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" + integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= + +array-includes@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.1.tgz#cdd67e6852bdf9c1215460786732255ed2459348" + integrity sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0" + is-string "^1.0.5" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= +array.prototype.flat@^1.2.1: + version "1.2.3" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b" + integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= + asn1.js@^4.0.0: version "4.10.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" @@ -168,7 +1221,19 @@ asn1.js@^4.0.0: inherits "^2.0.1" minimalistic-assert "^1.0.0" -assert@^1.4.0: +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +assert@^1.1.1: version "1.5.0" resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== @@ -181,16 +1246,93 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + async-each@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + atob@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +autoprefixer@^9.7.4: + version "9.7.4" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.7.4.tgz#f8bf3e06707d047f0641d87aee8cfb174b2a5378" + integrity sha512-g0Ya30YrMBAEZk60lp+qfX5YQllG+S5W3GYCFvyHTvhOki0AEQJLPEcIuGRsqVwLi8FvXPVtwTGhfr38hVpm0g== + dependencies: + browserslist "^4.8.3" + caniuse-lite "^1.0.30001020" + chalk "^2.4.2" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + postcss "^7.0.26" + postcss-value-parser "^4.0.2" + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e" + integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug== + +babel-plugin-dynamic-import-node@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f" + integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ== + dependencies: + object.assign "^4.1.0" + +babel-runtime@^6.11.6, babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babel-types@^6.15.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc= + dependencies: + babel-runtime "^6.26.0" + esutils "^2.0.2" + lodash "^4.17.4" + to-fast-properties "^1.0.3" + +babylon-walk@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/babylon-walk/-/babylon-walk-1.0.2.tgz#3b15a5ddbb482a78b4ce9c01c8ba181702d9d6ce" + integrity sha1-OxWl3btIKni0zpwByLoYFwLZ1s4= + dependencies: + babel-runtime "^6.11.6" + babel-types "^6.15.0" + lodash.clone "^4.5.0" + +bail@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" + integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ== + balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -214,6 +1356,13 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + binary-extensions@^1.0.0: version "1.13.1" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" @@ -238,18 +1387,10 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== -boxen@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" - integrity sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw== - dependencies: - ansi-align "^2.0.0" - camelcase "^4.0.0" - chalk "^2.0.1" - cli-boxes "^1.0.0" - string-width "^2.0.0" - term-size "^1.2.0" - widest-line "^2.0.0" +boolbase@^1.0.0, boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= brace-expansion@^1.1.7: version "1.1.11" @@ -275,29 +1416,37 @@ braces@^2.3.1, braces@^2.3.2: split-string "^3.0.2" to-regex "^3.0.1" +braces@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +brfs@^1.2.0: + version "1.6.1" + resolved "https://registry.yarnpkg.com/brfs/-/brfs-1.6.1.tgz#b78ce2336d818e25eea04a0947cba6d4fb8849c3" + integrity sha512-OfZpABRQQf+Xsmju8XE9bDjs+uU4vLREGolP7bDgcpsI17QREyZ4Bl+2KLxxx1kCgA0fAIhKQBaBYh+PEcCqYQ== + dependencies: + quote-stream "^1.0.1" + resolve "^1.1.5" + static-module "^2.2.0" + through2 "^2.0.0" + brorand@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= -browser-pack@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.1.0.tgz#c34ba10d0b9ce162b5af227c7131c92c2ecd5774" - integrity sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA== - dependencies: - JSONStream "^1.0.3" - combine-source-map "~0.8.0" - defined "^1.0.0" - safe-buffer "^5.1.1" - through2 "^2.0.0" - umd "^3.0.0" +browser-process-hrtime@^0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz#616f00faef1df7ec1b5bf9cfe2bdc3170f26c7b4" + integrity sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw== -browser-resolve@^1.11.0, browser-resolve@^1.7.0: - version "1.11.3" - resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" - integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ== - dependencies: - resolve "1.1.7" +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== browserify-aes@^1.0.0, browserify-aes@^1.0.4: version "1.2.0" @@ -351,66 +1500,21 @@ browserify-sign@^4.0.0: inherits "^2.0.1" parse-asn1 "^5.0.0" -browserify-zlib@~0.2.0: +browserify-zlib@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== dependencies: pako "~1.0.5" -browserify@^16.5.0: - version "16.5.0" - resolved "https://registry.yarnpkg.com/browserify/-/browserify-16.5.0.tgz#a1c2bc0431bec11fd29151941582e3f645ede881" - integrity sha512-6bfI3cl76YLAnCZ75AGu/XPOsqUhRyc0F/olGIJeCxtfxF2HvPKEcmjU9M8oAPxl4uBY1U7Nry33Q6koV3f2iw== +browserslist@^4.0.0, browserslist@^4.1.0, browserslist@^4.8.3, browserslist@^4.8.5: + version "4.8.7" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.8.7.tgz#ec8301ff415e6a42c949d0e66b405eb539c532d0" + integrity sha512-gFOnZNYBHrEyUML0xr5NJ6edFaaKbTFX9S9kQHlYfCP0Rit/boRIz4G+Avq6/4haEKJXdGGUnoolx+5MWW2BoA== dependencies: - JSONStream "^1.0.3" - assert "^1.4.0" - browser-pack "^6.0.1" - browser-resolve "^1.11.0" - browserify-zlib "~0.2.0" - buffer "^5.0.2" - cached-path-relative "^1.0.0" - concat-stream "^1.6.0" - console-browserify "^1.1.0" - constants-browserify "~1.0.0" - crypto-browserify "^3.0.0" - defined "^1.0.0" - deps-sort "^2.0.0" - domain-browser "^1.2.0" - duplexer2 "~0.1.2" - events "^2.0.0" - glob "^7.1.0" - has "^1.0.0" - htmlescape "^1.1.0" - https-browserify "^1.0.0" - inherits "~2.0.1" - insert-module-globals "^7.0.0" - labeled-stream-splicer "^2.0.0" - mkdirp "^0.5.0" - module-deps "^6.0.0" - os-browserify "~0.3.0" - parents "^1.0.1" - path-browserify "~0.0.0" - process "~0.11.0" - punycode "^1.3.2" - querystring-es3 "~0.2.0" - read-only-stream "^2.0.0" - readable-stream "^2.0.2" - resolve "^1.1.4" - shasum "^1.0.0" - shell-quote "^1.6.1" - stream-browserify "^2.0.0" - stream-http "^3.0.0" - string_decoder "^1.1.1" - subarg "^1.0.0" - syntax-error "^1.1.1" - through2 "^2.0.0" - timers-browserify "^1.0.1" - tty-browserify "0.0.1" - url "~0.11.0" - util "~0.10.1" - vm-browserify "^1.0.0" - xtend "^4.0.0" + caniuse-lite "^1.0.30001027" + electron-to-chromium "^1.3.349" + node-releases "^1.1.49" buffer-alloc-unsafe@^1.1.0: version "1.1.0" @@ -425,6 +1529,11 @@ buffer-alloc@^1.2.0: buffer-alloc-unsafe "^1.1.0" buffer-fill "^1.0.0" +buffer-equal@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-0.0.1.tgz#91bc74b11ea405bc916bc6aa908faafa5b4aac4b" + integrity sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs= + buffer-fill@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" @@ -440,13 +1549,14 @@ buffer-xor@^1.0.3: resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= -buffer@^5.0.2: - version "5.4.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.4.3.tgz#3fbc9c69eb713d323e3fc1a895eee0710c072115" - integrity sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A== +buffer@^4.3.0: + version "4.9.2" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" + integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== dependencies: base64-js "^1.0.2" ieee754 "^1.1.4" + isarray "^1.0.0" builtin-status-codes@^3.0.0: version "3.0.0" @@ -468,22 +1578,86 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" -cached-path-relative@^1.0.0, cached-path-relative@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.0.2.tgz#a13df4196d26776220cc3356eb147a52dba2c6db" - integrity sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg== - -camelcase@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" - integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= - -capture-stack-trace@^1.0.0: +call-me-maybe@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz#a6c0bbe1f38f3aa0b92238ecb6ff42c344d4135d" - integrity sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw== + resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" + integrity sha1-JtII6onje1y95gJQoV8DHBak1ms= -chalk@^2.0.1, chalk@^2.4.2: +caller-callsite@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" + integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= + dependencies: + callsites "^2.0.0" + +caller-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" + integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= + dependencies: + caller-callsite "^2.0.0" + +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase-keys@^6.1.1: + version "6.1.2" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.1.2.tgz#531a289aeea93249b63ec1249db9265f305041f7" + integrity sha512-QfFrU0CIw2oltVvpndW32kuJ/9YOJwUnmWrjlXt1nnJZHCaS9i6bfOpg9R4Lw8aZjStkJWM+jc0cdXjWBgVJSw== + dependencies: + camelcase "^5.3.1" + map-obj "^4.0.0" + quick-lru "^4.0.1" + +camelcase@^5.0.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +caniuse-api@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" + integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== + dependencies: + browserslist "^4.0.0" + caniuse-lite "^1.0.0" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001020, caniuse-lite@^1.0.30001027: + version "1.0.30001027" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001027.tgz#283e2ef17d94889cc216a22c6f85303d78ca852d" + integrity sha512-7xvKeErvXZFtUItTHgNtLgS9RJpVnwBlWX8jSo/BO8VsF6deszemZSkJJJA1KOKrXuzZH4WALpAJdq5EyfgMLg== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +ccount@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.0.5.tgz#ac82a944905a65ce204eb03023157edf29425c17" + integrity sha512-MOli1W+nfbPLlKEhInaxhRdp7KVLFxLN5ykwzHgLsLI3H3gs5jjFAK4Eoj3OzzcxCtumDaI8onoVDeQyWaNTkw== + +chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= + dependencies: + 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" + +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -492,12 +1666,45 @@ chalk@^2.0.1, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +character-entities-html4@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.4.tgz#0e64b0a3753ddbf1fdc044c5fd01d0199a02e125" + integrity sha512-HRcDxZuZqMx3/a+qrzxdBKBPUpxWEq9xw2OPZ3a/174ihfrQKVsFhqtthBInFy1zZ9GgZyFXOatNujm8M+El3g== + +character-entities-legacy@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" + integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== + +character-entities@^1.0.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b" + integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== + +character-reference-invalid@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" + integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + charenc@~0.0.1: version "0.0.2" resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= -chokidar@^2.1.8: +chokidar@^2.1.5: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== @@ -516,15 +1723,10 @@ chokidar@^2.1.8: optionalDependencies: fsevents "^1.2.7" -chownr@^1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142" - integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw== - -ci-info@^1.5.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" - integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== +chownr@^1.1.1, chownr@^1.1.3: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" @@ -544,11 +1746,6 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -cli-boxes@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" - integrity sha1-T6kXw+WclKAEzWH47lCdplFocUM= - cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" @@ -556,16 +1753,63 @@ cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" -cli-spinners@^2.0.0: +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-spinners@^1.1.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.3.1.tgz#002c1990912d0d59580c93bd36c056de99e4259a" + integrity sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg== + +cli-width@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.2.0.tgz#e8b988d9206c692302d8ee834e7a85c0144d8f77" - integrity sha512-tgU3fKwzYjiLEQgPMD9Jt+JjHVL9kW93FiIMX/l7rivvOD4/LL0Mf7gda3+4U2KJBloybwgj5KEoQgGRioMiKQ== + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" + integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= + +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + +clone-regexp@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clone-regexp/-/clone-regexp-2.2.0.tgz#7d65e00885cd8796405c35a737e7a86b7429e36f" + integrity sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q== + dependencies: + is-regexp "^2.0.0" clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= +clone@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= + +coa@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" + integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== + dependencies: + "@types/q" "^1.5.1" + chalk "^2.4.1" + q "^1.1.2" + +collapse-white-space@^1.0.2: + version "1.0.6" + resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287" + integrity sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ== + collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" @@ -574,27 +1818,62 @@ collection-visit@^1.0.0: map-visit "^1.0.0" object-visit "^1.0.0" -color-convert@^1.9.0: +color-convert@^1.9.0, color-convert@^1.9.1: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= -combine-source-map@^0.8.0, combine-source-map@~0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.8.0.tgz#a58d0df042c186fcf822a8e8015f5450d2d79a8b" - integrity sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos= +color-name@^1.0.0, color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-string@^1.5.2: + version "1.5.3" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" + integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== dependencies: - convert-source-map "~1.1.0" - inline-source-map "~0.6.0" - lodash.memoize "~3.0.3" - source-map "~0.5.3" + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/color/-/color-3.1.2.tgz#68148e7f85d41ad7649c5fa8c8106f098d229e10" + integrity sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg== + dependencies: + color-convert "^1.9.1" + color-string "^1.5.2" + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +command-exists@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.8.tgz#715acefdd1223b9c9b37110a149c6392c2852291" + integrity sha512-PM54PkseWbiiD/mMsbvW351/u+dafwTJ0ye2qB60G1aGQP9j3xK2gmMDc+R34L3nDtx4qMCitXT75mkbkGJDLw== + +commander@^2.11.0, commander@^2.19.0, commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== component-emitter@^1.2.1: version "1.3.0" @@ -606,7 +1885,7 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@^1.6.0, concat-stream@^1.6.1, concat-stream@~1.6.0: +concat-stream@~1.6.0: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -616,43 +1895,72 @@ concat-stream@^1.6.0, concat-stream@^1.6.1, concat-stream@~1.6.0: readable-stream "^2.2.2" typedarray "^0.0.6" -configstore@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.2.tgz#c6f25defaeef26df12dd33414b001fe81a543f8f" - integrity sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw== - dependencies: - dot-prop "^4.1.0" - graceful-fs "^4.1.2" - make-dir "^1.0.0" - unique-string "^1.0.0" - write-file-atomic "^2.0.0" - xdg-basedir "^3.0.0" - console-browserify@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== -constants-browserify@~1.0.0: +constants-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= -convert-source-map@~1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860" - integrity sha1-SCnId+n+SbMWHzvzZziI4gRpmGA= +contains-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" + integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= + +convert-source-map@^1.5.1, convert-source-map@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + dependencies: + safe-buffer "~5.1.1" copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= -core-util-is@~1.0.0: +core-js-compat@^3.6.2: + version "3.6.4" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.4.tgz#938476569ebb6cda80d339bcf199fae4f16fff17" + integrity sha512-zAa3IZPvsJ0slViBQ2z+vgyyTuhd3MFn1rBQjZSKVEgB0UMYhUkCj9jJUVPgGTGqWvsBVmfnruXgTcNyTlEiSA== + dependencies: + browserslist "^4.8.3" + semver "7.0.0" + +core-js@^2.4.0, core-js@^2.6.5: + version "2.6.11" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" + integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== + +core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= +cosmiconfig@^5.0.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" + integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== + dependencies: + import-fresh "^2.0.0" + is-directory "^0.3.1" + js-yaml "^3.13.1" + parse-json "^4.0.0" + +cosmiconfig@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" + integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.1.0" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.7.2" + create-ecdh@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" @@ -661,13 +1969,6 @@ create-ecdh@^4.0.0: bn.js "^4.1.0" elliptic "^6.0.0" -create-error-class@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" - integrity sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y= - dependencies: - capture-stack-trace "^1.0.0" - create-hash@^1.1.0, create-hash@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" @@ -691,12 +1992,14 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" -cross-spawn@^5.0.1: - version "5.1.0" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" - integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= +cross-spawn@^6.0.4, cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== dependencies: - lru-cache "^4.0.1" + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" shebang-command "^1.2.0" which "^1.2.9" @@ -705,7 +2008,7 @@ crypt@~0.0.1: resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= -crypto-browserify@^3.0.0: +crypto-browserify@^3.11.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== @@ -722,39 +2025,242 @@ crypto-browserify@^3.0.0: randombytes "^2.0.0" randomfill "^1.0.3" -crypto-random-string@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" - integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4= +css-color-names@0.0.4, css-color-names@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" + integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= -dash-ast@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/dash-ast/-/dash-ast-1.0.0.tgz#12029ba5fb2f8aa6f0a861795b23c1b4b6c27d37" - integrity sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA== +css-declaration-sorter@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz#c198940f63a76d7e36c1e71018b001721054cb22" + integrity sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA== + dependencies: + postcss "^7.0.1" + timsort "^0.3.0" -debug@^2.2.0, debug@^2.3.3: +css-modules-loader-core@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/css-modules-loader-core/-/css-modules-loader-core-1.1.0.tgz#5908668294a1becd261ae0a4ce21b0b551f21d16" + integrity sha1-WQhmgpShvs0mGuCkziGwtVHyHRY= + dependencies: + icss-replace-symbols "1.1.0" + postcss "6.0.1" + postcss-modules-extract-imports "1.1.0" + postcss-modules-local-by-default "1.2.0" + postcss-modules-scope "1.1.0" + postcss-modules-values "1.3.0" + +css-select-base-adapter@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" + integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== + +css-select@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef" + integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ== + dependencies: + boolbase "^1.0.0" + css-what "^3.2.1" + domutils "^1.7.0" + nth-check "^1.0.2" + +css-selector-tokenizer@^0.7.0: + version "0.7.1" + resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz#a177271a8bca5019172f4f891fc6eed9cbf68d5d" + integrity sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA== + dependencies: + cssesc "^0.1.0" + fastparse "^1.1.1" + regexpu-core "^1.0.0" + +css-tree@1.0.0-alpha.37: + version "1.0.0-alpha.37" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" + integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== + dependencies: + mdn-data "2.0.4" + source-map "^0.6.1" + +css-unit-converter@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.1.tgz#d9b9281adcfd8ced935bdbaba83786897f64e996" + integrity sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY= + +css-what@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.2.1.tgz#f4a8f12421064621b456755e34a03a2c22df5da1" + integrity sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw== + +cssesc@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4" + integrity sha1-yBSQPkViM3GgR3tAEJqq++6t27Q= + +cssesc@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703" + integrity sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +cssnano-preset-default@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz#51ec662ccfca0f88b396dcd9679cdb931be17f76" + integrity sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA== + dependencies: + css-declaration-sorter "^4.0.1" + cssnano-util-raw-cache "^4.0.1" + postcss "^7.0.0" + postcss-calc "^7.0.1" + postcss-colormin "^4.0.3" + postcss-convert-values "^4.0.1" + postcss-discard-comments "^4.0.2" + postcss-discard-duplicates "^4.0.2" + postcss-discard-empty "^4.0.1" + postcss-discard-overridden "^4.0.1" + postcss-merge-longhand "^4.0.11" + postcss-merge-rules "^4.0.3" + postcss-minify-font-values "^4.0.2" + postcss-minify-gradients "^4.0.2" + postcss-minify-params "^4.0.2" + postcss-minify-selectors "^4.0.2" + postcss-normalize-charset "^4.0.1" + postcss-normalize-display-values "^4.0.2" + postcss-normalize-positions "^4.0.2" + postcss-normalize-repeat-style "^4.0.2" + postcss-normalize-string "^4.0.2" + postcss-normalize-timing-functions "^4.0.2" + postcss-normalize-unicode "^4.0.1" + postcss-normalize-url "^4.0.1" + postcss-normalize-whitespace "^4.0.2" + postcss-ordered-values "^4.1.2" + postcss-reduce-initial "^4.0.3" + postcss-reduce-transforms "^4.0.2" + postcss-svgo "^4.0.2" + postcss-unique-selectors "^4.0.1" + +cssnano-util-get-arguments@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz#ed3a08299f21d75741b20f3b81f194ed49cc150f" + integrity sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8= + +cssnano-util-get-match@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz#c0e4ca07f5386bb17ec5e52250b4f5961365156d" + integrity sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0= + +cssnano-util-raw-cache@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz#b26d5fd5f72a11dfe7a7846fb4c67260f96bf282" + integrity sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA== + dependencies: + postcss "^7.0.0" + +cssnano-util-same-parent@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3" + integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q== + +cssnano@^4.0.0, cssnano@^4.1.10: + version "4.1.10" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.10.tgz#0ac41f0b13d13d465487e111b778d42da631b8b2" + integrity sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ== + dependencies: + cosmiconfig "^5.0.0" + cssnano-preset-default "^4.0.7" + is-resolvable "^1.0.0" + postcss "^7.0.0" + +csso@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/csso/-/csso-4.0.2.tgz#e5f81ab3a56b8eefb7f0092ce7279329f454de3d" + integrity sha512-kS7/oeNVXkHWxby5tHVxlhjizRCSv8QdU7hB2FpdAibDU8FjTAolhNjKNTiLzXtUrKT6HwClE81yXwEk1309wg== + dependencies: + css-tree "1.0.0-alpha.37" + +cssom@0.3.x, cssom@^0.3.4: + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^1.1.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.4.0.tgz#9d31328229d3c565c61e586b02041a28fccdccf1" + integrity sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA== + dependencies: + cssom "0.3.x" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +data-urls@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" + integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ== + dependencies: + abab "^2.0.0" + whatwg-mimetype "^2.2.0" + whatwg-url "^7.0.0" + +deasync@^0.1.14: + version "0.1.19" + resolved "https://registry.yarnpkg.com/deasync/-/deasync-0.1.19.tgz#e7ea89fcc9ad483367e8a48fe78f508ca86286e8" + integrity sha512-oh3MRktfnPlLysCPpBpKZZzb4cUC/p0aA3SyRGp15lN30juJBTo/CiD0d4fR+f1kBtUQoJj1NE9RPNWQ7BQ9Mg== + dependencies: + bindings "^1.5.0" + node-addon-api "^1.7.1" + +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@^3.2.6: +debug@3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== dependencies: ms "^2.1.1" +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +decamelize-keys@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" + integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= + dependencies: + decamelize "^1.1.0" + map-obj "^1.0.0" + +decamelize@^1.1.0, decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= defaults@^1.0.3: version "1.0.3" @@ -792,20 +2298,15 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" -defined@^1.0.0: +delayed-stream@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" - integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= -deps-sort@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/deps-sort/-/deps-sort-2.0.1.tgz#9dfdc876d2bcec3386b6829ac52162cda9fa208d" - integrity sha512-1orqXQr5po+3KI6kQb9A4jnXT1PBwggGl2d7Sq2xsnOeI9GPcE/tGcF9UiSZtZBM7MukY4cAh7MemS6tZYipfw== - dependencies: - JSONStream "^1.0.3" - shasum-object "^1.0.0" - subarg "^1.0.0" - through2 "^2.0.0" +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= des.js@^1.0.0: version "1.0.1" @@ -815,19 +2316,20 @@ des.js@^1.0.0: inherits "^2.0.1" minimalistic-assert "^1.0.0" -detective@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.0.tgz#feb2a77e85b904ecdea459ad897cc90a99bd2a7b" - integrity sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg== - dependencies: - acorn-node "^1.6.1" - defined "^1.0.0" - minimist "^1.1.1" +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +diff@3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== diff@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.1.tgz#0c667cb467ebbb5cea7f14f135cc2dba7780a8ff" - integrity sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q== + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== diffie-hellman@^5.0.0: version "5.0.3" @@ -838,29 +2340,114 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" -domain-browser@^1.2.0: +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +doctrine@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dom-serializer@0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" + integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== + dependencies: + domelementtype "^2.0.1" + entities "^2.0.0" + +domain-browser@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== -dot-prop@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57" - integrity sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ== - dependencies: - is-obj "^1.0.0" +domelementtype@1, domelementtype@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" + integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== -duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2: +domelementtype@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d" + integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ== + +domexception@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" + integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug== + dependencies: + webidl-conversions "^4.0.2" + +domhandler@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" + integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== + dependencies: + domelementtype "1" + +domutils@^1.5.1, domutils@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== + dependencies: + dom-serializer "0" + domelementtype "1" + +dot-prop@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb" + integrity sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A== + dependencies: + is-obj "^2.0.0" + +dotenv-expand@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" + integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA== + +dotenv@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-5.0.1.tgz#a5317459bd3d79ab88cff6e44057a6a3fbb1fcef" + integrity sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow== + +duplexer2@~0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" integrity sha1-ixLauHjA1p4+eJEFFmKjL8a93ME= dependencies: readable-stream "^2.0.2" -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" - integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +electron-to-chromium@^1.3.349: + version "1.3.351" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.351.tgz#78bcf8e9092013232b2fb72b9db423d96e92604c" + integrity sha512-L8zhV8k7Znp2q3wWXYDzCyfTBeGauEX0rX/FtgmnDgmvHRqwu9NVN614wOkXx9sDZmJZpNMBaEFMXTu/vbr+Kg== elliptic@^6.0.0: version "6.5.2" @@ -875,6 +2462,21 @@ elliptic@^6.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.0" +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -882,10 +2484,32 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" -es-abstract@^1.17.0-next.1: - version "1.17.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.0.tgz#f42a517d0036a5591dbb2c463591dc8bb50309b1" - integrity sha512-yYkE07YF+6SIBmg1MsJ9dlub5L48Ek7X0qz+c/CPCHS9EBXfESorzng4cJQjJW5/pB6vDF41u7F8vUhLVDqIug== +entities@^1.1.1, entities@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" + integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== + +entities@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" + integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== + +envinfo@^7.3.1: + version "7.5.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.5.0.tgz#91410bb6db262fb4f1409bd506e9ff57e91023f4" + integrity sha512-jDgnJaF/Btomk+m3PZDTTCb5XIIIX3zYItnCRfF73zVgvinLoRomuhi75Y4su0PtQxWz4v66XnLLckyvyJTOIQ== + +error-ex@^1.2.0, error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2: + version "1.17.4" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184" + integrity sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ== dependencies: es-to-primitive "^1.2.1" function-bind "^1.1.1" @@ -913,15 +2537,203 @@ es6-promisify@^6.0.0: resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-6.0.2.tgz#525c23725b8510f5f1f2feb5a1fbad93a93e29b4" integrity sha512-eO6vFm0JvqGzjWIQA6QVKjxpmELfhWbDUWHm1rPfIbn55mhKPiAa5xpLmQWJrNa629ZIeQ8ZvMAi13kvrjK6Mg== -escape-string-regexp@^1.0.5: +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -events@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/events/-/events-2.1.0.tgz#2a9a1e18e6106e0e812aa9ebd4a819b3c29c0ba5" - integrity sha512-3Zmiobend8P9DjmKAty0Era4jV8oJ0yGYe2nJJAxgymF9+N8F2m0hhZiMoWtcfepExzNKZumFU3ksdQbInGWCg== +escodegen@^1.11.0, escodegen@^1.11.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.1.tgz#ba01d0c8278b5e95a9a45350142026659027a457" + integrity sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ== + dependencies: + esprima "^4.0.1" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +escodegen@~1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.9.1.tgz#dbae17ef96c8e4bedb1356f4504fa4cc2f7cb7e2" + integrity sha512-6hTjO1NAWkHnDk3OqQ4YrCuwwmGHL9S3nPlzBOUG/R44rda3wLNrfvQ5fkSGjyhHFKM7ALPKcKGrwvCLe0lC7Q== + dependencies: + esprima "^3.1.3" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +eslint-config-prettier@^6.0.0: + version "6.10.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.10.0.tgz#7b15e303bf9c956875c948f6b21500e48ded6a7f" + integrity sha512-AtndijGte1rPILInUdHjvKEGbIV06NuvPrqlIEaEaWtbtvJh464mDeyGMdZEQMsGvC0ZVkiex1fSNcC4HAbRGg== + dependencies: + get-stdin "^6.0.0" + +eslint-import-resolver-node@^0.3.2: + version "0.3.3" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz#dbaa52b6b2816b50bc6711af75422de808e98404" + integrity sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg== + dependencies: + debug "^2.6.9" + resolve "^1.13.1" + +eslint-module-utils@^2.4.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.5.2.tgz#7878f7504824e1b857dd2505b59a8e5eda26a708" + integrity sha512-LGScZ/JSlqGKiT8OC+cYRxseMjyqt6QO54nl281CK93unD89ijSeRV6An8Ci/2nvWVKe8K/Tqdm75RQoIOCr+Q== + dependencies: + debug "^2.6.9" + pkg-dir "^2.0.0" + +eslint-plugin-import@^2.18.2: + version "2.20.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.20.1.tgz#802423196dcb11d9ce8435a5fc02a6d3b46939b3" + integrity sha512-qQHgFOTjguR+LnYRoToeZWT62XM55MBVXObHM6SKFd1VzDcX/vqT1kAz8ssqigh5eMj8qXcRoXXGZpPP6RfdCw== + dependencies: + array-includes "^3.0.3" + array.prototype.flat "^1.2.1" + contains-path "^0.1.0" + debug "^2.6.9" + doctrine "1.5.0" + eslint-import-resolver-node "^0.3.2" + eslint-module-utils "^2.4.1" + has "^1.0.3" + minimatch "^3.0.4" + object.values "^1.1.0" + read-pkg-up "^2.0.0" + resolve "^1.12.0" + +eslint-plugin-prettier@^3.1.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz#432e5a667666ab84ce72f945c72f77d996a5c9ba" + integrity sha512-GlolCC9y3XZfv3RQfwGew7NnuFDKsfI4lbvRK+PIIo23SFH+LemGs4cKwzAaRa+Mdb+lQO/STaIayno8T5sJJA== + dependencies: + prettier-linter-helpers "^1.0.0" + +eslint-scope@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" + integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-utils@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" + integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== + +eslint@^6.2.0: + version "6.8.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" + integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.10.0" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^5.0.0" + eslint-utils "^1.4.3" + eslint-visitor-keys "^1.1.0" + espree "^6.1.2" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.0.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^7.0.0" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.14" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.3" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^6.1.2" + strip-ansi "^5.2.0" + strip-json-comments "^3.0.1" + table "^5.2.3" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.2.tgz#6c272650932b4f91c3714e5e7b5f5e2ecf47262d" + integrity sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA== + dependencies: + acorn "^7.1.0" + acorn-jsx "^5.1.0" + eslint-visitor-keys "^1.1.0" + +esprima@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" + integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= + +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.1.0.tgz#c5c0b66f383e7656404f86b31334d72524eddb48" + integrity sha512-MxYW9xKmROWF672KqjO75sszsA8Mxhw06YFeS5VHlB98KDHbOSurm3ArsjO60Eaf3QmGMCP1yn+0JQkNLo/97Q== + dependencies: + estraverse "^4.0.0" + +esrecurse@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" + integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== + dependencies: + estraverse "^4.1.0" + +estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +esutils@^2.0.0, esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +events@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.1.0.tgz#84279af1b34cb75aa88bf5ff291f6d0bd9b31a59" + integrity sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg== evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: version "1.0.3" @@ -931,18 +2743,12 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" -execa@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" - integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= +execall@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/execall/-/execall-2.0.0.tgz#16a06b5fe5099df7d00be5d9c06eecded1663b45" + integrity sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow== dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" + clone-regexp "^2.1.0" expand-brackets@^2.1.4: version "2.1.4" @@ -972,6 +2778,20 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" +extend@^3.0.0, extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + extglob@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" @@ -986,16 +2806,105 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" -fast-safe-stringify@^2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" - integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +falafel@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/falafel/-/falafel-2.1.0.tgz#96bb17761daba94f46d001738b3cedf3a67fe06c" + integrity sha1-lrsXdh2rqU9G0AFzizzt86Z/4Gw= + dependencies: + acorn "^5.0.0" + foreach "^2.0.5" + isarray "0.0.1" + object-keys "^1.0.6" + +fast-deep-equal@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" + integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== + +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + +fast-glob@^2.2.2: + version "2.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" + integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== + dependencies: + "@mrmlnc/readdir-enhanced" "^2.2.1" + "@nodelib/fs.stat" "^1.1.2" + glob-parent "^3.1.0" + is-glob "^4.0.0" + merge2 "^1.2.3" + micromatch "^3.1.10" + +fast-glob@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.1.1.tgz#87ee30e9e9f3eb40d6f254a7997655da753d7c82" + integrity sha512-nTCREpBY8w8r+boyFYAx21iL6faSsQynliPHM4Uf56SbkyohCNxpVPEH9xrF5TXKy+IsjkPUHDKiUkzBVRXn9g== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.0" + merge2 "^1.3.0" + micromatch "^4.0.2" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +fastparse@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9" + integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ== + +fastq@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.6.0.tgz#4ec8a38f4ac25f21492673adb7eae9cfef47d1c2" + integrity sha512-jmxqQ3Z/nXoeyDmWAzF9kH1aGZSis6e/SbfPmJpUnyZ0ogr6iscHQaml4wsEepEWSdtmpy+eVXmCRIMpxaXqOA== + dependencies: + reusify "^1.0.0" + +figures@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.1.0.tgz#4b198dd07d8d71530642864af2d45dd9e459c4ec" + integrity sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg== + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== +filesize@^3.6.0: + version "3.6.1" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" + integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg== + fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -1006,11 +2915,80 @@ fill-range@^4.0.0: repeat-string "^1.6.1" to-regex-range "^2.1.0" +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-up@3.0.0, find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +find-up@^2.0.0, find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + +find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flat@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.0.tgz#090bec8b05e39cba309747f1d588f04dbaf98db2" + integrity sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw== + dependencies: + is-buffer "~2.0.3" + +flatted@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" + integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg== + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" @@ -1018,20 +2996,16 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + fs-constants@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== -fs-extra@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" - integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-extra@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" @@ -1041,6 +3015,13 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -1059,21 +3040,48 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -get-assigned-identifiers@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz#6dbf411de648cbaf8d9169ebb0d2d576191e2ff1" - integrity sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ== +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= -get-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" - integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= +gensync@^1.0.0-beta.1: + version "1.0.0-beta.1" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" + integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-port@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc" + integrity sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw= + +get-stdin@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" + integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== + +get-stdin@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-7.0.0.tgz#8d5de98f15171a125c5e516643c7a6d0ea8a96f6" + integrity sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ== get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + glob-parent@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" @@ -1082,7 +3090,31 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob@^7.1.0, glob@^7.1.3: +glob-parent@^5.0.0, glob-parent@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2" + integrity sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw== + dependencies: + is-glob "^4.0.1" + +glob-to-regexp@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" + integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= + +glob@7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== + dependencies: + 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@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -1094,40 +3126,116 @@ glob@^7.1.0, glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -global-dirs@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" - integrity sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU= +global-modules@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== dependencies: - ini "^1.3.4" + global-prefix "^3.0.0" -got@^6.7.1: - version "6.7.1" - resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" - integrity sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA= +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== dependencies: - create-error-class "^3.0.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - is-redirect "^1.0.0" - is-retry-allowed "^1.0.0" - is-stream "^1.0.0" - lowercase-keys "^1.0.0" - safe-buffer "^5.0.1" - timed-out "^4.0.0" - unzip-response "^2.0.1" - url-parse-lax "^1.0.0" + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^12.1.0: + version "12.3.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-12.3.0.tgz#1e564ee5c4dded2ab098b0f88f24702a3c56be13" + integrity sha512-wAfjdLgFsPZsklLJvOBUBmzYE8/CwhEqSBEMRXA3qxIiNtyqvjYurAtIfDh6chlEPUfmTY3MnZh5Hfh4q0UlIw== + dependencies: + type-fest "^0.8.1" + +globby@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.0.tgz#56fd0e9f0d4f8fb0c456f1ab0dee96e1380bc154" + integrity sha512-iuehFnR3xu5wBBtm4xi0dMe92Ob87ufyu/dHwpDYfbcpYpIbrO5OnS8M1vWvrBhSGEJ3/Ecj7gnX76P8YxpPEg== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.1.1" + ignore "^5.1.4" + merge2 "^1.3.0" + slash "^3.0.0" + +globjoin@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43" + integrity sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM= + +gonzales-pe@^4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/gonzales-pe/-/gonzales-pe-4.2.4.tgz#356ae36a312c46fe0f1026dd6cb539039f8500d2" + integrity sha512-v0Ts/8IsSbh9n1OJRnSfa7Nlxi4AkXIsWB6vPept8FDbL4bXn3FNuxjYtO/nmBGu7GDkL9MFeGebeSu6l55EPQ== + dependencies: + minimist "1.1.x" graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.3" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== +grapheme-breaker@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/grapheme-breaker/-/grapheme-breaker-0.3.2.tgz#5b9e6b78c3832452d2ba2bb1cb830f96276410ac" + integrity sha1-W55reMODJFLSuiuxy4MPlidkEKw= + dependencies: + brfs "^1.2.0" + unicode-trie "^0.3.1" + +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" + integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== + dependencies: + ajv "^6.5.5" + har-schema "^2.0.0" + +hard-rejection@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" + integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= + dependencies: + ansi-regex "^2.0.0" + +has-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo= + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + has-symbols@^1.0.0, has-symbols@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" @@ -1164,7 +3272,7 @@ has-values@^1.0.0: is-number "^3.0.0" kind-of "^4.0.0" -has@^1.0.0, has@^1.0.3: +has@^1.0.0, has@^1.0.1, has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== @@ -1187,6 +3295,16 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +hex-color-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" + integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== + hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -1196,10 +3314,88 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -htmlescape@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351" - integrity sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E= +hosted-git-info@^2.1.4: + version "2.8.5" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c" + integrity sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg== + +hsl-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e" + integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4= + +hsla-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" + integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= + +html-comment-regex@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" + integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== + +html-encoding-sniffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" + integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw== + dependencies: + whatwg-encoding "^1.0.1" + +html-tags@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-1.2.0.tgz#c78de65b5663aa597989dd2b7ab49200d7e4db98" + integrity sha1-x43mW1Zjqll5id0rerSSANfk25g= + +html-tags@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140" + integrity sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg== + +htmlnano@^0.2.2: + version "0.2.5" + resolved "https://registry.yarnpkg.com/htmlnano/-/htmlnano-0.2.5.tgz#134fd9548c7cbe51c8508ce434a3f9488cff1b0b" + integrity sha512-X1iPSwXG/iF9bVs+/obt2n6F64uH0ETkA8zp7qFDmLW9/+A6ueHGeb/+qD67T21qUY22owZPMdawljN50ajkqA== + dependencies: + cssnano "^4.1.10" + normalize-html-whitespace "^1.0.0" + posthtml "^0.12.0" + posthtml-render "^1.1.5" + purgecss "^1.4.0" + svgo "^1.3.2" + terser "^4.3.9" + uncss "^0.17.2" + +htmlparser2@^3.10.0, htmlparser2@^3.9.2: + version "3.10.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" + integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== + dependencies: + domelementtype "^1.3.1" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^3.1.1" + +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" httpolyglot@^0.1.2: version "0.1.2" @@ -1211,26 +3407,69 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= +iconv-lite@0.4.24, iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +icss-replace-symbols@1.1.0, icss-replace-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" + integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0= + ieee754@^1.1.4: version "1.1.13" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== -ignore-by-default@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" - integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk= +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -import-lazy@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" - integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= +ignore@^5.1.4: + version "5.1.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf" + integrity sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A== + +import-fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" + integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= + dependencies: + caller-path "^2.0.0" + resolve-from "^3.0.0" + +import-fresh@^3.0.0, import-fresh@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" + integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-lazy@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153" + integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw== imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +indexes-of@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" + integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -1239,7 +3478,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -1254,33 +3493,46 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@^1.3.4, ini@~1.3.0: +ini@^1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== -inline-source-map@~0.6.0: - version "0.6.2" - resolved "https://registry.yarnpkg.com/inline-source-map/-/inline-source-map-0.6.2.tgz#f9393471c18a79d1724f863fa38b586370ade2a5" - integrity sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU= +inquirer@^7.0.0: + version "7.0.4" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.0.4.tgz#99af5bde47153abca23f5c7fc30db247f39da703" + integrity sha512-Bu5Td5+j11sCkqfqmUTiwv+tWisMtP0L7Q8WrqA2C/BbBhy1YTdFrvjjlrKq8oagA/tLQBski2Gcx/Sqyi2qSQ== dependencies: - source-map "~0.5.3" + ansi-escapes "^4.2.1" + chalk "^2.4.2" + cli-cursor "^3.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.15" + mute-stream "0.0.8" + run-async "^2.2.0" + rxjs "^6.5.3" + string-width "^4.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" -insert-module-globals@^7.0.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/insert-module-globals/-/insert-module-globals-7.2.0.tgz#ec87e5b42728479e327bd5c5c71611ddfb4752ba" - integrity sha512-VE6NlW+WGn2/AeOMd496AHFYmE7eLKkUY6Ty31k4og5vmA3Fjuwe9v6ifH6Xx/Hz27QvdoMoviw1/pqWRB09Sw== +invariant@^2.2.2, invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== dependencies: - JSONStream "^1.0.3" - acorn-node "^1.5.2" - combine-source-map "^0.8.0" - concat-stream "^1.6.1" - is-buffer "^1.1.0" - path-is-absolute "^1.0.1" - process "~0.11.0" - through2 "^2.0.0" - undeclared-identifiers "^1.1.2" - xtend "^4.0.0" + loose-envify "^1.0.0" + +is-absolute-url@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" + integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY= + +is-absolute-url@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" + integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== is-accessor-descriptor@^0.1.6: version "0.1.6" @@ -1296,10 +3548,33 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" -is-arguments@^1.0.4: +is-alphabetical@^1.0.0: version "1.0.4" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" - integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== + resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" + integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== + +is-alphanumeric@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz#4a9cef71daf4c001c1d81d63d140cf53fd6889f4" + integrity sha1-Spzvcdr0wAHB2B1j0UDPU/1oifQ= + +is-alphanumerical@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" + integrity sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A== + dependencies: + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== is-binary-path@^1.0.0: version "1.0.1" @@ -1308,22 +3583,32 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" -is-buffer@^1.1.0, is-buffer@^1.1.5, is-buffer@~1.1.1: +is-buffer@^1.1.5, is-buffer@~1.1.1: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== +is-buffer@^2.0.0, is-buffer@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623" + integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A== + is-callable@^1.1.4, is-callable@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== -is-ci@^1.0.10: - version "1.2.1" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.2.1.tgz#e3779c8ee17fccf428488f6e281187f2e632841c" - integrity sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg== +is-color-stop@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" + integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U= dependencies: - ci-info "^1.5.0" + css-color-names "^0.0.4" + hex-color-regex "^1.1.0" + hsl-regex "^1.0.0" + hsla-regex "^1.0.0" + rgb-regex "^1.0.1" + rgba-regex "^1.0.0" is-data-descriptor@^0.1.4: version "0.1.4" @@ -1344,6 +3629,11 @@ is-date-object@^1.0.1: resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== +is-decimal@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" + integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== + is-descriptor@^0.1.0: version "0.1.6" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" @@ -1362,6 +3652,11 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-data-descriptor "^1.0.0" kind-of "^6.0.2" +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= + is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" @@ -1384,10 +3679,10 @@ is-fullwidth-code-point@^2.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= -is-generator-function@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522" - integrity sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw== +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-glob@^3.1.0: version "3.1.0" @@ -1396,25 +3691,24 @@ is-glob@^3.1.0: dependencies: is-extglob "^2.1.0" -is-glob@^4.0.0: +is-glob@^4.0.0, is-glob@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== dependencies: is-extglob "^2.1.1" -is-installed-globally@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80" - integrity sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA= - dependencies: - global-dirs "^0.1.0" - is-path-inside "^1.0.0" +is-hexadecimal@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" + integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== -is-npm@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" - integrity sha1-8vtjpl5JBbQGyGBydloaTceTufQ= +is-html@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-html/-/is-html-1.1.0.tgz#e04f1c18d39485111396f9a0273eab51af218464" + integrity sha1-4E8cGNOUhRETlvmgJz6rUa8hhGQ= + dependencies: + html-tags "^1.0.0" is-number@^3.0.0: version "3.0.0" @@ -1423,17 +3717,20 @@ is-number@^3.0.0: dependencies: kind-of "^3.0.2" -is-obj@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" - integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-path-inside@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" - integrity sha1-jvW33lBDej/cprToZe96pVy0gDY= - dependencies: - path-is-inside "^1.0.1" +is-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== + +is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" @@ -1442,10 +3739,10 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" -is-redirect@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" - integrity sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ= +is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= is-regex@^1.0.5: version "1.0.5" @@ -1454,15 +3751,27 @@ is-regex@^1.0.5: dependencies: has "^1.0.3" -is-retry-allowed@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" - integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== +is-regexp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-2.1.0.tgz#cd734a56864e23b956bf4e7c66c396a4c0b22c2d" + integrity sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA== -is-stream@^1.0.0, is-stream@^1.1.0: +is-resolvable@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" + integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== + +is-string@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== + +is-svg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75" + integrity sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ== + dependencies: + html-comment-regex "^1.1.0" is-symbol@^1.0.2: version "1.0.3" @@ -1471,12 +3780,42 @@ is-symbol@^1.0.2: dependencies: has-symbols "^1.0.1" +is-typedarray@^1.0.0, is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-url@^1.2.2: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" + integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== + +is-whitespace-character@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7" + integrity sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w== + is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== -isarray@1.0.0, isarray@~1.0.0: +is-word-character@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.4.tgz#ce0e73216f98599060592f62ff31354ddbeb0230" + integrity sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA== + +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= @@ -1498,12 +3837,109 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= -json-stable-stringify@~0.0.0: - version "0.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz#611c23e814db375527df851193db59dd2af27f45" - integrity sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U= +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@3.13.1, js-yaml@^3.10.0, js-yaml@^3.13.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== dependencies: - jsonify "~0.0.0" + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +jsdom@^14.1.0: + version "14.1.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-14.1.0.tgz#916463b6094956b0a6c1782c94e380cd30e1981b" + integrity sha512-O901mfJSuTdwU2w3Sn+74T+RnDVP+FuV5fH8tcPWyqrseRAb0s5xOtPgCFiPOtLcyK7CLIJwPyD83ZqQWvA5ng== + dependencies: + abab "^2.0.0" + acorn "^6.0.4" + acorn-globals "^4.3.0" + array-equal "^1.0.0" + cssom "^0.3.4" + cssstyle "^1.1.1" + data-urls "^1.1.0" + domexception "^1.0.1" + escodegen "^1.11.0" + html-encoding-sniffer "^1.0.2" + nwsapi "^2.1.3" + parse5 "5.1.0" + pn "^1.1.0" + request "^2.88.0" + request-promise-native "^1.0.5" + saxes "^3.1.9" + symbol-tree "^3.2.2" + tough-cookie "^2.5.0" + w3c-hr-time "^1.0.1" + w3c-xmlserializer "^1.1.2" + webidl-conversions "^4.0.2" + whatwg-encoding "^1.0.5" + whatwg-mimetype "^2.3.0" + whatwg-url "^7.0.0" + ws "^6.1.2" + xml-name-validator "^3.0.0" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= + +json-parse-better-errors@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + +json5@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.1.tgz#81b6cb04e9ba496f1c7005d07b4368a2638f90b6" + integrity sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ== + dependencies: + minimist "^1.2.0" jsonfile@^4.0.0: version "4.0.0" @@ -1512,22 +3948,15 @@ jsonfile@^4.0.0: optionalDependencies: graceful-fs "^4.1.6" -jsonify@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" - integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= - -jsonparse@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" - integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= - -jszip@2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/jszip/-/jszip-2.6.0.tgz#7fb3e9c2f11c8a9840612db5dabbc8cf3a7534b7" - integrity sha1-f7PpwvEciphAYS212rvIzzp1NLc= +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= dependencies: - pako "~1.0.0" + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" @@ -1549,56 +3978,139 @@ kind-of@^5.0.0: integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" - integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== -labeled-stream-splicer@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz#42a41a16abcd46fd046306cf4f2c3576fffb1c21" - integrity sha512-Ca4LSXFFZUjPScRaqOcFxneA0VpKZr4MMYCljyQr4LIewTLb3Y0IUTIsnBBsVubIeEfxeSZpSjSsRM8APEQaAw== +known-css-properties@^0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.18.0.tgz#d6e00b56ee1d5b0d171fd86df1583cfb012c521f" + integrity sha512-69AgJ1rQa7VvUsd2kpvVq+VeObDuo3zrj0CzM5Slmf6yduQFAI2kXPDQJR2IE/u6MSAUOJrwSzjg5vlz8qcMiw== + +leaked-handles@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/leaked-handles/-/leaked-handles-5.2.0.tgz#67228e90293b7e0ee36c4190e1f541cddece627f" + integrity sha1-ZyKOkCk7fg7jbEGQ4fVBzd7OYn8= dependencies: - inherits "^2.0.1" - stream-splicer "^2.0.0" + process "^0.10.0" + weakmap-shim "^1.1.0" + xtend "^4.0.0" -latest-version@^3.0.0: +leven@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15" - integrity sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU= + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levenary@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/levenary/-/levenary-1.1.1.tgz#842a9ee98d2075aa7faeedbe32679e9205f46f77" + integrity sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ== dependencies: - package-json "^4.0.0" + leven "^3.1.0" -lodash.memoize@~3.0.3: - version "3.0.4" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f" - integrity sha1-LcvSwofLwKVcxCMovQxzYVDVPj8= +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" -log-symbols@^2.2.0: +lines-and-columns@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" + integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= + +load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + strip-bom "^3.0.0" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +lodash.clone@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-4.5.0.tgz#195870450f5a13192478df4bc3d23d2dea1907b6" + integrity sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y= + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= + +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= + +lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" + integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== + +log-symbols@2.2.0, log-symbols@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== dependencies: chalk "^2.0.1" -lowercase-keys@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" - integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== - -lru-cache@^4.0.1: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== +log-symbols@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" + integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" + chalk "^2.4.2" -make-dir@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" - integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== +longest-streak@^2.0.1: + version "2.0.4" + resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.4.tgz#b8599957da5b5dab64dee3fe316fa774597d90e4" + integrity sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg== + +loose-envify@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== dependencies: - pify "^3.0.0" + js-tokens "^3.0.0 || ^4.0.0" + +magic-string@^0.22.4: + version "0.22.5" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.22.5.tgz#8e9cf5afddf44385c1da5bc2a6a0dbd10b03657e" + integrity sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w== + dependencies: + vlq "^0.2.2" make-error@^1.1.1: version "1.3.5" @@ -1610,6 +4122,16 @@ map-cache@^0.2.2: resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= +map-obj@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= + +map-obj@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.1.0.tgz#b91221b542734b9f14256c0132c897c5d7256fd5" + integrity sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g== + map-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" @@ -1617,6 +4139,21 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +markdown-escapes@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535" + integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg== + +markdown-table@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.3.tgz#9fcb69bcfdb8717bfd0398c6ec2d93036ef8de60" + integrity sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q== + +mathml-tag-names@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" + integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== + md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -1635,7 +4172,48 @@ md5@^2.2.1: crypt "~0.0.1" is-buffer "~1.1.1" -micromatch@^3.1.10, micromatch@^3.1.4: +mdast-util-compact@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mdast-util-compact/-/mdast-util-compact-1.0.4.tgz#d531bb7667b5123abf20859be086c4d06c894593" + integrity sha512-3YDMQHI5vRiS2uygEFYaqckibpJtKq5Sj2c8JioeOQBU6INpKbdWzfyLqFFnDwEcEnRFIdMsguzs5pC1Jp4Isg== + dependencies: + unist-util-visit "^1.1.0" + +mdn-data@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" + integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== + +meow@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/meow/-/meow-6.0.1.tgz#f9b3f912c9aa039142cebcf74315129f4cd1ce1c" + integrity sha512-kxGTFgT/b7/oSRSQsJ0qsT5IMU+bgZ1eAdSA3kIV7onkW0QWo/hL5RbGlMfvBjHJKPE1LaPX0kdecYFiqYWjUw== + dependencies: + "@types/minimist" "^1.2.0" + camelcase-keys "^6.1.1" + decamelize-keys "^1.1.0" + hard-rejection "^2.0.0" + minimist-options "^4.0.1" + normalize-package-data "^2.5.0" + read-pkg-up "^7.0.0" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.8.1" + yargs-parser "^16.1.0" + +merge-source-map@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.0.4.tgz#a5de46538dae84d4114cc5ea02b4772a6346701f" + integrity sha1-pd5GU42uhNQRTMXqArR3KmNGcB8= + dependencies: + source-map "^0.5.6" + +merge2@^1.2.3, merge2@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81" + integrity sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw== + +micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -1654,6 +4232,14 @@ micromatch@^3.1.10, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" +micromatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" + integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== + dependencies: + braces "^3.0.1" + picomatch "^2.0.5" + miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" @@ -1662,11 +4248,38 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" +mime-db@1.43.0: + version "1.43.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" + integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ== + +mime-types@^2.1.12, mime-types@~2.1.19: + version "2.1.26" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" + integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ== + dependencies: + mime-db "1.43.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +min-indent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.0.tgz#cfc45c37e9ec0d8f0a0ec3dd4ef7f7c3abe39256" + integrity sha1-z8RcN+nsDY8KDsPdTvf3w6vjklY= + minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" @@ -1677,23 +4290,51 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= -minimatch@^3.0.4: +minimatch@3.0.4, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" +minimist-options@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.0.2.tgz#29c4021373ded40d546186725e57761e4b1984a7" + integrity sha512-seq4hpWkYSUh1y7NXxzucwAN9yVlBc3Upgdjz8vLCP97jG8kaOmzYrVH/m7tQ1NYD1wdtZbSLfdy4zFmRWuc/w== + dependencies: + arrify "^1.0.1" + is-plain-obj "^1.1.0" + minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= -minimist@^1.1.0, minimist@^1.1.1, minimist@^1.2.0: +minimist@1.1.x: + version "1.1.3" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.1.3.tgz#3bedfd91a92d39016fcfaa1c681e8faa1a1efda8" + integrity sha1-O+39kaktOQFvz6ocaB6Pqhoe/ag= + +minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= +minipass@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.1.tgz#7607ce778472a185ad6d89082aa2070f79cedcd5" + integrity sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w== + dependencies: + yallist "^4.0.0" + +minizlib@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.0.tgz#fd52c645301ef09a63a2c209697c294c6ce02cf3" + integrity sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + mixin-deep@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" @@ -1702,44 +4343,67 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@^0.5.0, mkdirp@^0.5.1: +mkdirp@0.5.1, mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= dependencies: minimist "0.0.8" -module-deps@^6.0.0: +mkdirp@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.3.tgz#4cf2e30ad45959dddea53ad97d518b6c8205e1ea" + integrity sha512-6uCP4Qc0sWsgMLy1EOqqS/3rjDHOEnsStVr/4vtAIK2Y5i2kA7lFFejYrpIyiN9w0pYf4ckeCYT9f1r1P9KX5g== + +mocha@^6.2.0: version "6.2.2" - resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-6.2.2.tgz#d8a15c2265dfc119153c29bb47386987d0ee423b" - integrity sha512-a9y6yDv5u5I4A+IPHTnqFxcaKr4p50/zxTjcQJaX2ws9tN/W6J6YXnEKhqRyPhl494dkcxx951onSKVezmI+3w== + resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.2.2.tgz#5d8987e28940caf8957a7d7664b910dc5b2fea20" + integrity sha512-FgDS9Re79yU1xz5d+C4rv1G7QagNGHZ+iXF81hO8zY35YZZcLEsJVfFolfsqKFWunATEvNzMK0r/CwWd/szO9A== dependencies: - JSONStream "^1.0.3" - browser-resolve "^1.7.0" - cached-path-relative "^1.0.2" - concat-stream "~1.6.0" - defined "^1.0.0" - detective "^5.2.0" - duplexer2 "^0.1.2" - inherits "^2.0.1" - parents "^1.0.0" - readable-stream "^2.0.2" - resolve "^1.4.0" - stream-combiner2 "^1.1.1" - subarg "^1.0.0" - through2 "^2.0.0" - xtend "^4.0.0" + ansi-colors "3.2.3" + browser-stdout "1.3.1" + debug "3.2.6" + diff "3.5.0" + escape-string-regexp "1.0.5" + find-up "3.0.0" + glob "7.1.3" + growl "1.10.5" + he "1.2.0" + js-yaml "3.13.1" + log-symbols "2.2.0" + minimatch "3.0.4" + mkdirp "0.5.1" + ms "2.1.1" + node-environment-flags "1.0.5" + object.assign "4.1.0" + strip-json-comments "2.0.1" + supports-color "6.0.0" + which "1.3.1" + wide-align "1.1.3" + yargs "13.3.0" + yargs-parser "13.1.1" + yargs-unparser "1.6.0" ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + nan@^2.12.1: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" @@ -1762,33 +4426,84 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" -node-fetch@^2.3.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" - integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -nodemon@^1.19.1: - version "1.19.4" - resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.19.4.tgz#56db5c607408e0fdf8920d2b444819af1aae0971" - integrity sha512-VGPaqQBNk193lrJFotBU8nvWZPqEZY2eIzymy2jjY0fJ9qIsxA0sxQ8ATPl0gZC645gijYEc1jtZvpS8QWzJGQ== - dependencies: - chokidar "^2.1.8" - debug "^3.2.6" - ignore-by-default "^1.0.1" - minimatch "^3.0.4" - pstree.remy "^1.1.7" - semver "^5.7.1" - supports-color "^5.5.0" - touch "^3.1.0" - undefsafe "^2.0.2" - update-notifier "^2.5.0" +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -nopt@~1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" - integrity sha1-bd0hvSoxQXuScn3Vhfim83YI6+4= +node-addon-api@^1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.1.tgz#cf813cd69bb8d9100f6bdca6755fc268f54ac492" + integrity sha512-2+DuKodWvwRTrCfKOeR24KIc5unKjOh8mz17NCzVnHWfjAdDqbfbjqh7gUT+BkXBRQM52+xCHciKWonJ3CbJMQ== + +node-environment-flags@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a" + integrity sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ== dependencies: - abbrev "1" + object.getownpropertydescriptors "^2.0.3" + semver "^5.7.0" + +node-forge@^0.7.1: + version "0.7.6" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac" + integrity sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw== + +node-libs-browser@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" + integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== + dependencies: + assert "^1.1.1" + browserify-zlib "^0.2.0" + buffer "^4.3.0" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + crypto-browserify "^3.11.0" + domain-browser "^1.1.1" + events "^3.0.0" + https-browserify "^1.0.0" + os-browserify "^0.3.0" + path-browserify "0.0.1" + process "^0.11.10" + punycode "^1.2.4" + querystring-es3 "^0.2.0" + readable-stream "^2.3.3" + stream-browserify "^2.0.1" + stream-http "^2.7.2" + string_decoder "^1.0.0" + timers-browserify "^2.0.4" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.11.0" + vm-browserify "^1.0.1" + +node-releases@^1.1.49: + version "1.1.49" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.49.tgz#67ba5a3fac2319262675ef864ed56798bb33b93e" + integrity sha512-xH8t0LS0disN0mtRCh+eByxFPie+msJUBL/lJDBuap53QGiYPa9joh83K4pCZgWJ+2L4b9h88vCVdXQ60NO2bg== + dependencies: + semver "^6.3.0" + +normalize-html-whitespace@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/normalize-html-whitespace/-/normalize-html-whitespace-1.0.0.tgz#5e3c8e192f1b06c3b9eee4b7e7f28854c7601e34" + integrity sha512-9ui7CGtOOlehQu0t/OhhlmDyc71mKVlv+4vF+me4iZLPrNtRL2xoquEdfZxasC/bdQi/Hr3iTrpyRKIG+ocabA== + +normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" normalize-path@^2.1.1: version "2.1.1" @@ -1802,12 +4517,42 @@ normalize-path@^3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= + +normalize-selector@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/normalize-selector/-/normalize-selector-0.2.0.tgz#d0b145eb691189c63a78d201dc4fdb1293ef0c03" + integrity sha1-0LFF62kRicY6eNIB3E/bEpPvDAM= + +normalize-url@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" + integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== + +nth-check@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" + integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== dependencies: - path-key "^2.0.0" + boolbase "~1.0.0" + +num2fraction@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" + integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= + +nwsapi@^2.1.3: + version "2.2.0" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" + integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== object-assign@^4.1.1: version "4.1.1" @@ -1828,7 +4573,12 @@ object-inspect@^1.7.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== -object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: +object-inspect@~1.4.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.4.1.tgz#37ffb10e71adaf3748d05f713b4c9452f402cbc4" + integrity sha512-wqdhLpfCUbEsoEwl3FXwGyv8ief1k/1aUdIPCqVnupM6e8l63BEJdiF/0swtn04/8p05tG/T0FrpTlfwvljOdw== + +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.0.6, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== @@ -1840,7 +4590,7 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@^4.1.0: +object.assign@4.1.0, object.assign@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== @@ -1850,15 +4600,13 @@ object.assign@^4.1.0: has-symbols "^1.0.0" object-keys "^1.0.11" -object.entries@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.1.tgz#ee1cf04153de02bb093fec33683900f57ce5399b" - integrity sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ== +object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" + integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg== dependencies: define-properties "^1.1.3" es-abstract "^1.17.0-next.1" - function-bind "^1.1.1" - has "^1.0.3" object.pick@^1.3.0: version "1.3.0" @@ -1867,6 +4615,23 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" +object.values@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" + integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + has "^1.0.3" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -1881,54 +4646,180 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" -ora@^3.2.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318" - integrity sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg== +onetime@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5" + integrity sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q== dependencies: - chalk "^2.4.2" + mimic-fn "^2.1.0" + +opn@^5.1.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" + integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== + dependencies: + is-wsl "^1.1.0" + +optionator@^0.8.1, optionator@^0.8.3: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +ora@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-2.1.0.tgz#6caf2830eb924941861ec53a173799e008b51e5b" + integrity sha512-hNNlAd3gfv/iPmsNxYoAPLvxg7HuPozww7fFonMZvL84tP6Ox5igfk5j/+a9rtJJwqMgKK+JgWsAQik5o0HTLA== + dependencies: + chalk "^2.3.1" cli-cursor "^2.1.0" - cli-spinners "^2.0.0" + cli-spinners "^1.1.0" log-symbols "^2.2.0" - strip-ansi "^5.2.0" + strip-ansi "^4.0.0" wcwidth "^1.0.1" -os-browserify@~0.3.0: +os-browserify@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= -os-tmpdir@^1.0.1: +os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= -p-finally@^1.0.0: +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + +p-limit@^2.0.0, p-limit@^2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e" + integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ== + dependencies: + p-try "^2.0.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-try@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= -package-json@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/package-json/-/package-json-4.0.1.tgz#8869a0401253661c4c4ca3da6c2121ed555f5eed" - integrity sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0= +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +pako@^0.2.5: + version "0.2.9" + resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" + integrity sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU= + +pako@~1.0.5: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + +parcel-bundler@^1.12.4: + version "1.12.4" + resolved "https://registry.yarnpkg.com/parcel-bundler/-/parcel-bundler-1.12.4.tgz#31223f4ab4d00323a109fce28d5e46775409a9ee" + integrity sha512-G+iZGGiPEXcRzw0fiRxWYCKxdt/F7l9a0xkiU4XbcVRJCSlBnioWEwJMutOCCpoQmaQtjB4RBHDGIHN85AIhLQ== dependencies: - got "^6.7.1" - registry-auth-token "^3.0.1" - registry-url "^3.0.3" - semver "^5.1.0" + "@babel/code-frame" "^7.0.0" + "@babel/core" "^7.4.4" + "@babel/generator" "^7.4.4" + "@babel/parser" "^7.4.4" + "@babel/plugin-transform-flow-strip-types" "^7.4.4" + "@babel/plugin-transform-modules-commonjs" "^7.4.4" + "@babel/plugin-transform-react-jsx" "^7.0.0" + "@babel/preset-env" "^7.4.4" + "@babel/runtime" "^7.4.4" + "@babel/template" "^7.4.4" + "@babel/traverse" "^7.4.4" + "@babel/types" "^7.4.4" + "@iarna/toml" "^2.2.0" + "@parcel/fs" "^1.11.0" + "@parcel/logger" "^1.11.1" + "@parcel/utils" "^1.11.0" + "@parcel/watcher" "^1.12.1" + "@parcel/workers" "^1.11.0" + ansi-to-html "^0.6.4" + babylon-walk "^1.0.2" + browserslist "^4.1.0" + chalk "^2.1.0" + clone "^2.1.1" + command-exists "^1.2.6" + commander "^2.11.0" + core-js "^2.6.5" + cross-spawn "^6.0.4" + css-modules-loader-core "^1.1.0" + cssnano "^4.0.0" + deasync "^0.1.14" + dotenv "^5.0.0" + dotenv-expand "^5.1.0" + envinfo "^7.3.1" + fast-glob "^2.2.2" + filesize "^3.6.0" + get-port "^3.2.0" + htmlnano "^0.2.2" + is-glob "^4.0.0" + is-url "^1.2.2" + js-yaml "^3.10.0" + json5 "^1.0.1" + micromatch "^3.0.4" + mkdirp "^0.5.1" + node-forge "^0.7.1" + node-libs-browser "^2.0.0" + opn "^5.1.0" + postcss "^7.0.11" + postcss-value-parser "^3.3.1" + posthtml "^0.11.2" + posthtml-parser "^0.4.0" + posthtml-render "^1.1.3" + resolve "^1.4.0" + semver "^5.4.1" + serialize-to-js "^3.0.0" + serve-static "^1.12.4" + source-map "0.6.1" + terser "^3.7.3" + v8-compile-cache "^2.0.0" + ws "^5.1.1" -pako@~1.0.0, pako@~1.0.5: - version "1.0.10" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732" - integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw== - -parents@^1.0.0, parents@^1.0.1: +parent-module@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/parents/-/parents-1.0.1.tgz#fedd4d2bf193a77745fe71e371d73c3307d9c751" - integrity sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E= + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== dependencies: - path-platform "~0.11.15" + callsites "^3.0.0" parse-asn1@^5.0.0: version "5.1.5" @@ -1942,12 +4833,59 @@ parse-asn1@^5.0.0: pbkdf2 "^3.0.3" safe-buffer "^5.1.1" +parse-entities@^1.0.2, parse-entities@^1.1.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-1.2.2.tgz#c31bf0f653b6661354f8973559cb86dd1d5edf50" + integrity sha512-NzfpbxW/NPrzZ/yYSoQxyqUZMZXIdCfE0OIN4ESsnptHJECoUk3FZktxNuzQf4tjt5UEopnxpYJbvYuxIFDdsg== + dependencies: + character-entities "^1.0.0" + character-entities-legacy "^1.0.0" + character-reference-invalid "^1.0.0" + is-alphanumerical "^1.0.0" + is-decimal "^1.0.0" + is-hexadecimal "^1.0.0" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +parse-json@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.0.0.tgz#73e5114c986d143efa3712d4ea24db9a4266f60f" + integrity sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + lines-and-columns "^1.1.6" + +parse5@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" + integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= -path-browserify@~0.0.0: +path-browserify@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== @@ -1957,17 +4895,22 @@ path-dirname@^1.0.0: resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= -path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-is-inside@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= - -path-key@^2.0.0: +path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= @@ -1977,10 +4920,17 @@ path-parse@^1.0.6: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== -path-platform@~0.11.15: - version "0.11.15" - resolved "https://registry.yarnpkg.com/path-platform/-/path-platform-0.11.15.tgz#e864217f74c36850f0852b78dc7bf7d4a5721bf2" - integrity sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I= +path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= + dependencies: + pify "^2.0.0" + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== pbkdf2@^3.0.3: version "3.0.17" @@ -1994,49 +4944,545 @@ pbkdf2@^3.0.3: sha.js "^2.4.8" pem@^1.14.2: - version "1.14.3" - resolved "https://registry.yarnpkg.com/pem/-/pem-1.14.3.tgz#347e5a5c194a5f7612b88083e45042fcc4fb4901" - integrity sha512-Q+AMVMD3fzeVvZs5PHeI+pVt0hgZY2fjhkliBW43qyONLgCXPVk1ryim43F9eupHlNGLJNT5T/NNrzhUdiC5Zg== + version "1.14.4" + resolved "https://registry.yarnpkg.com/pem/-/pem-1.14.4.tgz#a68c70c6e751ccc5b3b5bcd7af78b0aec1177ff9" + integrity sha512-v8lH3NpirgiEmbOqhx0vwQTxwi0ExsiWBGYh0jYNq7K6mQuO4gI6UEFlr6fLAdv9TPXRt6GqiwE37puQdIDS8g== dependencies: es6-promisify "^6.0.0" md5 "^2.2.1" os-tmpdir "^1.0.1" - which "^1.3.1" + which "^2.0.2" -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +physical-cpu-count@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/physical-cpu-count/-/physical-cpu-count-2.0.0.tgz#18de2f97e4bf7a9551ad7511942b5496f7aba660" + integrity sha1-GN4vl+S/epVRrXURlCtUlverpmA= + +picomatch@^2.0.5: + version "2.2.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a" + integrity sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA== + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= + dependencies: + find-up "^2.1.0" + +pn@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" + integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= -prepend-http@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" - integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= +postcss-calc@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.1.tgz#36d77bab023b0ecbb9789d84dcb23c4941145436" + integrity sha512-oXqx0m6tb4N3JGdmeMSc/i91KppbYsFZKdH0xMOqK8V1rJlzrKlTdokz8ozUXLVejydRN6u2IddxpcijRj2FqQ== + dependencies: + css-unit-converter "^1.1.1" + postcss "^7.0.5" + postcss-selector-parser "^5.0.0-rc.4" + postcss-value-parser "^3.3.1" + +postcss-colormin@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.3.tgz#ae060bce93ed794ac71264f08132d550956bd381" + integrity sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw== + dependencies: + browserslist "^4.0.0" + color "^3.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-convert-values@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz#ca3813ed4da0f812f9d43703584e449ebe189a7f" + integrity sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ== + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-discard-comments@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz#1fbabd2c246bff6aaad7997b2b0918f4d7af4033" + integrity sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg== + dependencies: + postcss "^7.0.0" + +postcss-discard-duplicates@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz#3fe133cd3c82282e550fc9b239176a9207b784eb" + integrity sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ== + dependencies: + postcss "^7.0.0" + +postcss-discard-empty@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz#c8c951e9f73ed9428019458444a02ad90bb9f765" + integrity sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w== + dependencies: + postcss "^7.0.0" + +postcss-discard-overridden@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz#652aef8a96726f029f5e3e00146ee7a4e755ff57" + integrity sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg== + dependencies: + postcss "^7.0.0" + +postcss-html@^0.36.0: + version "0.36.0" + resolved "https://registry.yarnpkg.com/postcss-html/-/postcss-html-0.36.0.tgz#b40913f94eaacc2453fd30a1327ad6ee1f88b204" + integrity sha512-HeiOxGcuwID0AFsNAL0ox3mW6MHH5cstWN1Z3Y+n6H+g12ih7LHdYxWwEA/QmrebctLjo79xz9ouK3MroHwOJw== + dependencies: + htmlparser2 "^3.10.0" + +postcss-jsx@^0.36.4: + version "0.36.4" + resolved "https://registry.yarnpkg.com/postcss-jsx/-/postcss-jsx-0.36.4.tgz#37a68f300a39e5748d547f19a747b3257240bd50" + integrity sha512-jwO/7qWUvYuWYnpOb0+4bIIgJt7003pgU3P6nETBLaOyBXuTD55ho21xnals5nBrlpTIFodyd3/jBi6UO3dHvA== + dependencies: + "@babel/core" ">=7.2.2" + +postcss-less@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/postcss-less/-/postcss-less-3.1.4.tgz#369f58642b5928ef898ffbc1a6e93c958304c5ad" + integrity sha512-7TvleQWNM2QLcHqvudt3VYjULVB49uiW6XzEUFmvwHzvsOEF5MwBrIXZDJQvJNFGjJQTzSzZnDoCJ8h/ljyGXA== + dependencies: + postcss "^7.0.14" + +postcss-markdown@^0.36.0: + version "0.36.0" + resolved "https://registry.yarnpkg.com/postcss-markdown/-/postcss-markdown-0.36.0.tgz#7f22849ae0e3db18820b7b0d5e7833f13a447560" + integrity sha512-rl7fs1r/LNSB2bWRhyZ+lM/0bwKv9fhl38/06gF6mKMo/NPnp55+K1dSTosSVjFZc0e1ppBlu+WT91ba0PMBfQ== + dependencies: + remark "^10.0.1" + unist-util-find-all-after "^1.0.2" + +postcss-media-query-parser@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz#27b39c6f4d94f81b1a73b8f76351c609e5cef244" + integrity sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ= + +postcss-merge-longhand@^4.0.11: + version "4.0.11" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24" + integrity sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw== + dependencies: + css-color-names "0.0.4" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + stylehacks "^4.0.0" + +postcss-merge-rules@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz#362bea4ff5a1f98e4075a713c6cb25aefef9a650" + integrity sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ== + dependencies: + browserslist "^4.0.0" + caniuse-api "^3.0.0" + cssnano-util-same-parent "^4.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + vendors "^1.0.0" + +postcss-minify-font-values@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz#cd4c344cce474343fac5d82206ab2cbcb8afd5a6" + integrity sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg== + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-minify-gradients@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz#93b29c2ff5099c535eecda56c4aa6e665a663471" + integrity sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q== + dependencies: + cssnano-util-get-arguments "^4.0.0" + is-color-stop "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-minify-params@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz#6b9cef030c11e35261f95f618c90036d680db874" + integrity sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg== + dependencies: + alphanum-sort "^1.0.0" + browserslist "^4.0.0" + cssnano-util-get-arguments "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + uniqs "^2.0.0" + +postcss-minify-selectors@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz#e2e5eb40bfee500d0cd9243500f5f8ea4262fbd8" + integrity sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g== + dependencies: + alphanum-sort "^1.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + +postcss-modules-extract-imports@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz#b614c9720be6816eaee35fb3a5faa1dba6a05ddb" + integrity sha1-thTJcgvmgW6u41+zpfqh26agXds= + dependencies: + postcss "^6.0.1" + +postcss-modules-local-by-default@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069" + integrity sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk= + dependencies: + css-selector-tokenizer "^0.7.0" + postcss "^6.0.1" + +postcss-modules-scope@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90" + integrity sha1-1upkmUx5+XtipytCb75gVqGUu5A= + dependencies: + css-selector-tokenizer "^0.7.0" + postcss "^6.0.1" + +postcss-modules-values@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20" + integrity sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA= + dependencies: + icss-replace-symbols "^1.1.0" + postcss "^6.0.1" + +postcss-normalize-charset@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz#8b35add3aee83a136b0471e0d59be58a50285dd4" + integrity sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g== + dependencies: + postcss "^7.0.0" + +postcss-normalize-display-values@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz#0dbe04a4ce9063d4667ed2be476bb830c825935a" + integrity sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ== + dependencies: + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-positions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz#05f757f84f260437378368a91f8932d4b102917f" + integrity sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA== + dependencies: + cssnano-util-get-arguments "^4.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-repeat-style@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz#c4ebbc289f3991a028d44751cbdd11918b17910c" + integrity sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q== + dependencies: + cssnano-util-get-arguments "^4.0.0" + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-string@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz#cd44c40ab07a0c7a36dc5e99aace1eca4ec2690c" + integrity sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA== + dependencies: + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-timing-functions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz#8e009ca2a3949cdaf8ad23e6b6ab99cb5e7d28d9" + integrity sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A== + dependencies: + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-unicode@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz#841bd48fdcf3019ad4baa7493a3d363b52ae1cfb" + integrity sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg== + dependencies: + browserslist "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-url@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz#10e437f86bc7c7e58f7b9652ed878daaa95faae1" + integrity sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA== + dependencies: + is-absolute-url "^2.0.0" + normalize-url "^3.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-whitespace@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz#bf1d4070fe4fcea87d1348e825d8cc0c5faa7d82" + integrity sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA== + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-ordered-values@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz#0cf75c820ec7d5c4d280189559e0b571ebac0eee" + integrity sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw== + dependencies: + cssnano-util-get-arguments "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-reduce-initial@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz#7fd42ebea5e9c814609639e2c2e84ae270ba48df" + integrity sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA== + dependencies: + browserslist "^4.0.0" + caniuse-api "^3.0.0" + has "^1.0.0" + postcss "^7.0.0" + +postcss-reduce-transforms@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz#17efa405eacc6e07be3414a5ca2d1074681d4e29" + integrity sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg== + dependencies: + cssnano-util-get-match "^4.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-reporter@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-reporter/-/postcss-reporter-6.0.1.tgz#7c055120060a97c8837b4e48215661aafb74245f" + integrity sha512-LpmQjfRWyabc+fRygxZjpRxfhRf9u/fdlKf4VHG4TSPbV2XNsuISzYW1KL+1aQzx53CAppa1bKG4APIB/DOXXw== + dependencies: + chalk "^2.4.1" + lodash "^4.17.11" + log-symbols "^2.2.0" + postcss "^7.0.7" + +postcss-resolve-nested-selector@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz#29ccbc7c37dedfac304e9fff0bf1596b3f6a0e4e" + integrity sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4= + +postcss-safe-parser@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-4.0.2.tgz#a6d4e48f0f37d9f7c11b2a581bf00f8ba4870b96" + integrity sha512-Uw6ekxSWNLCPesSv/cmqf2bY/77z11O7jZGPax3ycZMFU/oi2DMH9i89AdHc1tRwFg/arFoEwX0IS3LCUxJh1g== + dependencies: + postcss "^7.0.26" + +postcss-sass@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/postcss-sass/-/postcss-sass-0.4.2.tgz#7d1f8ddf6960d329de28fb3ff43c9c42013646bc" + integrity sha512-hcRgnd91OQ6Ot9R90PE/khUDCJHG8Uxxd3F7Y0+9VHjBiJgNv7sK5FxyHMCBtoLmmkzVbSj3M3OlqUfLJpq0CQ== + dependencies: + gonzales-pe "^4.2.4" + postcss "^7.0.21" + +postcss-scss@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-2.0.0.tgz#248b0a28af77ea7b32b1011aba0f738bda27dea1" + integrity sha512-um9zdGKaDZirMm+kZFKKVsnKPF7zF7qBAtIfTSnZXD1jZ0JNZIxdB6TxQOjCnlSzLRInVl2v3YdBh/M881C4ug== + dependencies: + postcss "^7.0.0" + +postcss-selector-parser@6.0.2, postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c" + integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg== + dependencies: + cssesc "^3.0.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-selector-parser@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz#b310f5c4c0fdaf76f94902bbaa30db6aa84f5270" + integrity sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA== + dependencies: + dot-prop "^5.2.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-selector-parser@^5.0.0-rc.4: + version "5.0.0" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz#249044356697b33b64f1a8f7c80922dddee7195c" + integrity sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ== + dependencies: + cssesc "^2.0.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-svgo@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.2.tgz#17b997bc711b333bab143aaed3b8d3d6e3d38258" + integrity sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw== + dependencies: + is-svg "^3.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + svgo "^1.0.0" + +postcss-syntax@^0.36.2: + version "0.36.2" + resolved "https://registry.yarnpkg.com/postcss-syntax/-/postcss-syntax-0.36.2.tgz#f08578c7d95834574e5593a82dfbfa8afae3b51c" + integrity sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w== + +postcss-unique-selectors@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac" + integrity sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg== + dependencies: + alphanum-sort "^1.0.0" + postcss "^7.0.0" + uniqs "^2.0.0" + +postcss-value-parser@^3.0.0, postcss-value-parser@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" + integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== + +postcss-value-parser@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz#482282c09a42706d1fc9a069b73f44ec08391dc9" + integrity sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ== + +postcss@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.1.tgz#000dbd1f8eef217aa368b9a212c5fc40b2a8f3f2" + integrity sha1-AA29H47vIXqjaLmiEsX8QLKo8/I= + dependencies: + chalk "^1.1.3" + source-map "^0.5.6" + supports-color "^3.2.3" + +postcss@^6.0.1: + version "6.0.23" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324" + integrity sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag== + dependencies: + chalk "^2.4.1" + source-map "^0.6.1" + supports-color "^5.4.0" + +postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.11, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.21, postcss@^7.0.26, postcss@^7.0.5, postcss@^7.0.7: + version "7.0.26" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.26.tgz#5ed615cfcab35ba9bbb82414a4fa88ea10429587" + integrity sha512-IY4oRjpXWYshuTDFxMVkJDtWIk2LhsTlu8bZnbEJA4+bYT16Lvpo8Qv6EvDumhYRgzjZl489pmsY3qVgJQ08nA== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + +posthtml-parser@^0.4.0, posthtml-parser@^0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.4.2.tgz#a132bbdf0cd4bc199d34f322f5c1599385d7c6c1" + integrity sha512-BUIorsYJTvS9UhXxPTzupIztOMVNPa/HtAm9KHni9z6qEfiJ1bpOBL5DfUOL9XAc3XkLIEzBzpph+Zbm4AdRAg== + dependencies: + htmlparser2 "^3.9.2" + +posthtml-render@^1.1.3, posthtml-render@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/posthtml-render/-/posthtml-render-1.1.5.tgz#387934e85438a3de77085fbc7d264efb00bd0e0f" + integrity sha512-yvt54j0zCBHQVEFAuR+yHld8CZrCa/E1Z/OcFNCV1IEWTLVxT8O7nYnM4IIw1CD4r8kaRd3lc42+0lgCKgm87w== + +posthtml@^0.11.2: + version "0.11.6" + resolved "https://registry.yarnpkg.com/posthtml/-/posthtml-0.11.6.tgz#e349d51af7929d0683b9d8c3abd8166beecc90a8" + integrity sha512-C2hrAPzmRdpuL3iH0TDdQ6XCc9M7Dcc3zEW5BLerY65G4tWWszwv6nG/ksi6ul5i2mx22ubdljgktXCtNkydkw== + dependencies: + posthtml-parser "^0.4.1" + posthtml-render "^1.1.5" + +posthtml@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/posthtml/-/posthtml-0.12.0.tgz#6e2a2fcd774eaed1a419a95c5cc3a92b676a40a6" + integrity sha512-aNUEP/SfKUXAt+ghG51LC5MmafChBZeslVe/SSdfKIgLGUVRE68mrMF4V8XbH07ZifM91tCSuxY3eHIFLlecQw== + dependencies: + posthtml-parser "^0.4.1" + posthtml-render "^1.1.5" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier@^1.18.2: + version "1.19.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" + integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== + +private@^0.1.6: + version "0.1.8" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -process@~0.11.0: +process@^0.10.0: + version "0.10.1" + resolved "https://registry.yarnpkg.com/process/-/process-0.10.1.tgz#842457cc51cfed72dc775afeeafb8c6034372725" + integrity sha1-hCRXzFHP7XLcd1r+6vuMYDQ3JyU= + +process@^0.11.10: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== -pstree.remy@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.7.tgz#c76963a28047ed61542dc361aa26ee55a7fa15f3" - integrity sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A== +psl@^1.1.28: + version "1.7.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c" + integrity sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ== public-encrypt@^4.0.0: version "4.0.3" @@ -2063,12 +5509,37 @@ punycode@1.3.2: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= -punycode@^1.3.2: +punycode@^1.2.4: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= -querystring-es3@~0.2.0: +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +purgecss@^1.4.0: + version "1.4.2" + resolved "https://registry.yarnpkg.com/purgecss/-/purgecss-1.4.2.tgz#67ab50cb4f5c163fcefde56002467c974e577f41" + integrity sha512-hkOreFTgiyMHMmC2BxzdIw5DuC6kxAbP/gGOGd3MEsF3+5m69rIvUEPaxrnoUtfODTFKe9hcXjGwC6jcjoyhOw== + dependencies: + glob "^7.1.3" + postcss "^7.0.14" + postcss-selector-parser "^6.0.0" + yargs "^14.0.0" + +q@^1.1.2: + version "1.5.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= @@ -2078,6 +5549,20 @@ querystring@0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= +quick-lru@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" + integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== + +quote-stream@^1.0.1, quote-stream@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/quote-stream/-/quote-stream-1.0.2.tgz#84963f8c9c26b942e153feeb53aae74652b7e0b2" + integrity sha1-hJY/jJwmuULhU/7rU6rnRlK34LI= + dependencies: + buffer-equal "0.0.1" + minimist "^1.1.3" + through2 "^2.0.0" + randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -2093,24 +5578,48 @@ randomfill@^1.0.3: randombytes "^2.0.5" safe-buffer "^5.1.0" -rc@^1.0.1, rc@^1.1.6: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -read-only-stream@^2.0.0: +read-pkg-up@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/read-only-stream/-/read-only-stream-2.0.0.tgz#2724fd6a8113d73764ac288d4386270c1dbf17f0" - integrity sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A= + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= dependencies: - readable-stream "^2.0.2" + find-up "^2.0.0" + read-pkg "^2.0.0" -readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@~2.3.6: +read-pkg-up@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" + integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== + dependencies: + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" + +read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= + dependencies: + load-json-file "^2.0.0" + normalize-package-data "^2.3.2" + path-type "^2.0.0" + +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" + +readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.3, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -2123,10 +5632,10 @@ readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@~2.3.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.0.1, readable-stream@^3.0.6, readable-stream@^3.1.1: - version "3.4.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc" - integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ== +readable-stream@^3.0.1, readable-stream@^3.1.1: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" @@ -2141,6 +5650,43 @@ readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== + dependencies: + indent-string "^4.0.0" + strip-indent "^3.0.0" + +regenerate-unicode-properties@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e" + integrity sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA== + dependencies: + regenerate "^1.4.0" + +regenerate@^1.2.1, regenerate@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" + integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== + +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== + +regenerator-runtime@^0.13.2: + version "0.13.3" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5" + integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw== + +regenerator-transform@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.1.tgz#3b2fce4e1ab7732c08f665dfdb314749c7ddd2fb" + integrity sha512-flVuee02C3FKRISbxhXl9mGzdbWUVHubl1SMaknjxkFB1/iqpJhArQUvRxOOPEc/9tAiX0BaQ28FJH10E4isSQ== + dependencies: + private "^0.1.6" + regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" @@ -2149,20 +5695,110 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -registry-auth-token@^3.0.1: - version "3.4.0" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.4.0.tgz#d7446815433f5d5ed6431cd5dca21048f66b397e" - integrity sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A== - dependencies: - rc "^1.1.6" - safe-buffer "^5.0.1" +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== -registry-url@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" - integrity sha1-PU74cPc93h138M+aOBQyRE4XSUI= +regexpp@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.0.0.tgz#dd63982ee3300e67b41c1956f850aa680d9d330e" + integrity sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g== + +regexpu-core@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b" + integrity sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs= dependencies: - rc "^1.0.1" + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + +regexpu-core@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.6.0.tgz#2037c18b327cfce8a6fea2a4ec441f2432afb8b6" + integrity sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg== + dependencies: + regenerate "^1.4.0" + regenerate-unicode-properties "^8.1.0" + regjsgen "^0.5.0" + regjsparser "^0.6.0" + unicode-match-property-ecmascript "^1.0.4" + unicode-match-property-value-ecmascript "^1.1.0" + +regjsgen@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" + integrity sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc= + +regjsgen@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.1.tgz#48f0bf1a5ea205196929c0d9798b42d1ed98443c" + integrity sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg== + +regjsparser@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" + integrity sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw= + dependencies: + jsesc "~0.5.0" + +regjsparser@^0.6.0: + version "0.6.3" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.3.tgz#74192c5805d35e9f5ebe3c1fb5b40d40a8a38460" + integrity sha512-8uZvYbnfAtEm9Ab8NTb3hdLwL4g/LQzEYP7Xs27T96abJCCE2d6r3cPZPQEsLKy0vRSGVNG+/zVGtLr86HQduA== + dependencies: + jsesc "~0.5.0" + +remark-parse@^6.0.0: + version "6.0.3" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-6.0.3.tgz#c99131052809da482108413f87b0ee7f52180a3a" + integrity sha512-QbDXWN4HfKTUC0hHa4teU463KclLAnwpn/FBn87j9cKYJWWawbiLgMfP2Q4XwhxxuuuOxHlw+pSN0OKuJwyVvg== + dependencies: + collapse-white-space "^1.0.2" + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + is-whitespace-character "^1.0.0" + is-word-character "^1.0.0" + markdown-escapes "^1.0.0" + parse-entities "^1.1.0" + repeat-string "^1.5.4" + state-toggle "^1.0.0" + trim "0.0.1" + trim-trailing-lines "^1.0.0" + unherit "^1.0.4" + unist-util-remove-position "^1.0.0" + vfile-location "^2.0.0" + xtend "^4.0.1" + +remark-stringify@^6.0.0: + version "6.0.4" + resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-6.0.4.tgz#16ac229d4d1593249018663c7bddf28aafc4e088" + integrity sha512-eRWGdEPMVudijE/psbIDNcnJLRVx3xhfuEsTDGgH4GsFF91dVhw5nhmnBppafJ7+NWINW6C7ZwWbi30ImJzqWg== + dependencies: + ccount "^1.0.0" + is-alphanumeric "^1.0.0" + is-decimal "^1.0.0" + is-whitespace-character "^1.0.0" + longest-streak "^2.0.1" + markdown-escapes "^1.0.0" + markdown-table "^1.1.0" + mdast-util-compact "^1.0.0" + parse-entities "^1.0.2" + repeat-string "^1.5.4" + state-toggle "^1.0.0" + stringify-entities "^1.0.1" + unherit "^1.0.4" + xtend "^4.0.1" + +remark@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/remark/-/remark-10.0.1.tgz#3058076dc41781bf505d8978c291485fe47667df" + integrity sha512-E6lMuoLIy2TyiokHprMjcWNJ5UxfGQjaMSMhV+f4idM625UjjK4j798+gPs5mfjzDE6vL0oFKVeZM6gZVSVrzQ== + dependencies: + remark-parse "^6.0.0" + remark-stringify "^6.0.0" + unified "^7.0.0" remove-trailing-separator@^1.0.1: version "1.1.0" @@ -2174,25 +5810,92 @@ repeat-element@^1.1.2: resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== -repeat-string@^1.6.1: +repeat-string@^1.5.4, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= +replace-ext@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" + integrity sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs= + +request-promise-core@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9" + integrity sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ== + dependencies: + lodash "^4.17.15" + +request-promise-native@^1.0.5: + version "1.0.8" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.8.tgz#a455b960b826e44e2bf8999af64dff2bfe58cb36" + integrity sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ== + dependencies: + request-promise-core "1.1.3" + stealthy-require "^1.1.1" + tough-cookie "^2.3.3" + +request@^2.88.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + 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.3" + 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.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha1-six699nWiBvItuZTM17rywoYh0g= + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" - integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= - -resolve@^1.1.4, resolve@^1.4.0: - version "1.14.2" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.14.2.tgz#dbf31d0fa98b1f29aa5169783b9c290cb865fea2" - integrity sha512-EjlOBLBO1kxsUxsKjLt7TAECyKW6fOh1VRkykQkKGzcBbjjPIxBqGh0jf7GJ3k/f5mxMqW3htMD3WdTUVtW8HQ== +resolve@^1.1.5, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.3.2, resolve@^1.4.0: + version "1.15.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8" + integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w== dependencies: path-parse "^1.0.6" @@ -2204,11 +5907,48 @@ restore-cursor@^2.0.0: onetime "^2.0.0" signal-exit "^3.0.2" +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== +reusify@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rgb-regex@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" + integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE= + +rgba-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" + integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= + +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +rimraf@^2.6.2: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" @@ -2217,6 +5957,25 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" +run-async@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" + integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= + dependencies: + is-promise "^2.1.0" + +run-parallel@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" + integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q== + +rxjs@^6.5.3: + version "6.5.4" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c" + integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q== + dependencies: + tslib "^1.9.0" + safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1, safe-buffer@~5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" @@ -2236,18 +5995,82 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" -semver-diff@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" - integrity sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY= - dependencies: - semver "^5.0.3" +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -semver@^5.0.3, semver@^5.1.0, semver@^5.7.1: +sax@~1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +saxes@^3.1.9: + version "3.1.11" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-3.1.11.tgz#d59d1fd332ec92ad98a2e0b2ee644702384b1c5b" + integrity sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g== + dependencies: + xmlchars "^2.1.1" + +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.7.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== +semver@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + +semver@^6.1.2, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.1.3.tgz#e4345ce73071c53f336445cfc19efb1c311df2a6" + integrity sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA== + +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +serialize-to-js@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/serialize-to-js/-/serialize-to-js-3.1.1.tgz#b3e77d0568ee4a60bfe66287f991e104d3a1a4ac" + integrity sha512-F+NGU0UHMBO4Q965tjw7rvieNVjlH6Lqi2emq/Lc9LUURYJbiCzmpi4Cy1OOjjVPtxu0c+NE85LU6968Wko5ZA== + +serve-static@^1.12.4: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" @@ -2258,7 +6081,17 @@ set-value@^2.0.0, set-value@^2.0.1: is-plain-object "^2.0.3" split-string "^3.0.1" -sha.js@^2.4.0, sha.js@^2.4.8, sha.js@~2.4.4: +setimmediate@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +sha.js@^2.4.0, sha.js@^2.4.8: version "2.4.11" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== @@ -2266,20 +6099,10 @@ sha.js@^2.4.0, sha.js@^2.4.8, sha.js@~2.4.4: inherits "^2.0.1" safe-buffer "^5.0.1" -shasum-object@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shasum-object/-/shasum-object-1.0.0.tgz#0b7b74ff5b66ecf9035475522fa05090ac47e29e" - integrity sha512-Iqo5rp/3xVi6M4YheapzZhhGPVs0yZwHj7wvwQ1B9z8H6zk+FEnI7y3Teq7qwnekfEhu8WmG2z0z4iWZaxLWVg== - dependencies: - fast-safe-stringify "^2.0.7" - -shasum@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/shasum/-/shasum-1.0.2.tgz#e7012310d8f417f4deb5712150e5678b87ae565f" - integrity sha1-5wEjENj0F/TetXEhUOVni4euVl8= - dependencies: - json-stable-stringify "~0.0.0" - sha.js "~2.4.4" +shallow-copy@~0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/shallow-copy/-/shallow-copy-0.0.1.tgz#415f42702d73d810330292cc5ee86eae1a11a170" + integrity sha1-QV9CcC1z2BAzApLMXuhurhoRoXA= shebang-command@^1.2.0: version "1.2.0" @@ -2293,20 +6116,31 @@ shebang-regex@^1.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= -shell-quote@^1.6.1: - version "1.7.2" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" - integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== - -signal-exit@^3.0.0, signal-exit@^3.0.2: +signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= -simple-concat@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6" - integrity sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY= +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= + dependencies: + is-arrayish "^0.3.1" + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" snapdragon-node@^2.0.1: version "2.1.1" @@ -2349,7 +6183,7 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@^0.5.6: +source-map-support@^0.5.6, source-map-support@~0.5.10, source-map-support@~0.5.12: version "0.5.16" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== @@ -2362,15 +6196,46 @@ source-map-url@^0.4.0: resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= -source-map@^0.5.6, source-map@~0.5.3: +source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.5.0, source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= -source-map@^0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +spdx-correct@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" + integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977" + integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA== + +spdx-expression-parse@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" + integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.5" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654" + integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q== + +specificity@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/specificity/-/specificity-0.4.1.tgz#aab5e645012db08ba182e151165738d00887b019" + integrity sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg== split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" @@ -2379,6 +6244,43 @@ split-string@^3.0.1, split-string@^3.0.2: dependencies: extend-shallow "^3.0.0" +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + 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" + +stable@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" + integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== + +state-toggle@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.3.tgz#e123b16a88e143139b09c6852221bc9815917dfe" + integrity sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ== + +static-eval@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.0.3.tgz#cb62fc79946bd4d5f623a45ad428233adace4d72" + integrity sha512-zsxDGucfAh8T339sSKgpFbvg15Fms2IVaJGC+jqp0bVsxhcpM+iMeAI8weNo8dmf4OblgifTBUoyk1vGVtYw2w== + dependencies: + escodegen "^1.11.1" + static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" @@ -2387,7 +6289,37 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" -stream-browserify@^2.0.0: +static-module@^2.2.0: + version "2.2.5" + resolved "https://registry.yarnpkg.com/static-module/-/static-module-2.2.5.tgz#bd40abceae33da6b7afb84a0e4329ff8852bfbbf" + integrity sha512-D8vv82E/Kpmz3TXHKG8PPsCPg+RAX6cbCOyvjM6x04qZtQ47EtJFVwRsdov3n5d6/6ynrOY9XB4JkaZwB2xoRQ== + dependencies: + concat-stream "~1.6.0" + convert-source-map "^1.5.1" + duplexer2 "~0.1.4" + escodegen "~1.9.0" + falafel "^2.1.0" + has "^1.0.1" + magic-string "^0.22.4" + merge-source-map "1.0.4" + object-inspect "~1.4.0" + quote-stream "~1.0.2" + readable-stream "~2.3.3" + shallow-copy "~0.0.1" + static-eval "^2.0.0" + through2 "~2.0.3" + +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +stealthy-require@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" + integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= + +stream-browserify@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== @@ -2395,33 +6327,18 @@ stream-browserify@^2.0.0: inherits "~2.0.1" readable-stream "^2.0.2" -stream-combiner2@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/stream-combiner2/-/stream-combiner2-1.1.1.tgz#fb4d8a1420ea362764e21ad4780397bebcb41cbe" - integrity sha1-+02KFCDqNidk4hrUeAOXvry0HL4= - dependencies: - duplexer2 "~0.1.0" - readable-stream "^2.0.2" - -stream-http@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-3.1.0.tgz#22fb33fe9b4056b4eccf58bd8f400c4b993ffe57" - integrity sha512-cuB6RgO7BqC4FBYzmnvhob5Do3wIdIsXAgGycHJnW+981gHqoYcYz9lqjJrk8WXRddbwPuqPYRl+bag6mYv4lw== +stream-http@^2.7.2: + version "2.8.3" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" + integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== dependencies: builtin-status-codes "^3.0.0" inherits "^2.0.1" - readable-stream "^3.0.6" + readable-stream "^2.3.6" + to-arraybuffer "^1.0.0" xtend "^4.0.0" -stream-splicer@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/stream-splicer/-/stream-splicer-2.0.1.tgz#0b13b7ee2b5ac7e0609a7463d83899589a363fcd" - integrity sha512-Xizh4/NPuYSyAXyT7g8IvdJ9HJpxIGL9PjyhtywCZvvP0OPIdqyrr4dMikeuvY8xahpdKEBlBTySe583totajg== - dependencies: - inherits "^2.0.1" - readable-stream "^2.0.2" - -string-width@^2.0.0, string-width@^2.1.1: +"string-width@^1.0.2 || 2": version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -2429,6 +6346,24 @@ string-width@^2.0.0, string-width@^2.1.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + string.prototype.trimleft@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74" @@ -2445,7 +6380,7 @@ string.prototype.trimright@^2.1.1: define-properties "^1.1.3" function-bind "^1.1.1" -string_decoder@^1.1.1: +string_decoder@^1.0.0, string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== @@ -2459,6 +6394,23 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +stringify-entities@^1.0.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-1.3.2.tgz#a98417e5471fd227b3e45d3db1861c11caf668f7" + integrity sha512-nrBAQClJAPN2p+uGCVJRPIPakKeKWZ9GtBCmormE7pWOSlHat7+x5A8gx85M7HM5Dt0BP3pP5RhVW77WdbJJ3A== + dependencies: + character-entities-html4 "^1.0.0" + character-entities-legacy "^1.0.0" + is-alphanumerical "^1.0.0" + is-hexadecimal "^1.0.0" + +strip-ansi@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + strip-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" @@ -2466,43 +6418,200 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" -strip-ansi@^5.2.0: +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== dependencies: ansi-regex "^4.1.0" -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" -strip-json-comments@~2.0.1: +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + +strip-json-comments@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -subarg@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2" - integrity sha1-9izxdYHplrSPyWVpn1TAauJouNI= - dependencies: - minimist "^1.1.0" +strip-json-comments@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" + integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== -supports-color@^5.3.0, supports-color@^5.5.0: +style-search@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902" + integrity sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI= + +stylehacks@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" + integrity sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g== + dependencies: + browserslist "^4.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + +stylelint-config-recommended@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-3.0.0.tgz#e0e547434016c5539fe2650afd58049a2fd1d657" + integrity sha512-F6yTRuc06xr1h5Qw/ykb2LuFynJ2IxkKfCMf+1xqPffkxh0S09Zc902XCffcsw/XMFq/OzQ1w54fLIDtmRNHnQ== + +stylelint@^13.0.0: + version "13.2.0" + resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-13.2.0.tgz#b6f5b67b9a9a51f1fd105ab916952456d93826b4" + integrity sha512-isf31yjkm0DQesx+Yk1b/WQpFkf1MicwaAVR22Hprx9HRFGhhEkWdrVCyrkK6HFymL0rhzynG97Tu53q/WCsAg== + dependencies: + autoprefixer "^9.7.4" + balanced-match "^1.0.0" + chalk "^3.0.0" + cosmiconfig "^6.0.0" + debug "^4.1.1" + execall "^2.0.0" + file-entry-cache "^5.0.1" + get-stdin "^7.0.0" + global-modules "^2.0.0" + globby "^11.0.0" + globjoin "^0.1.4" + html-tags "^3.1.0" + ignore "^5.1.4" + import-lazy "^4.0.0" + imurmurhash "^0.1.4" + known-css-properties "^0.18.0" + leven "^3.1.0" + lodash "^4.17.15" + log-symbols "^3.0.0" + mathml-tag-names "^2.1.3" + meow "^6.0.0" + micromatch "^4.0.2" + normalize-selector "^0.2.0" + postcss "^7.0.26" + postcss-html "^0.36.0" + postcss-jsx "^0.36.4" + postcss-less "^3.1.4" + postcss-markdown "^0.36.0" + postcss-media-query-parser "^0.2.3" + postcss-reporter "^6.0.1" + postcss-resolve-nested-selector "^0.1.1" + postcss-safe-parser "^4.0.1" + postcss-sass "^0.4.2" + postcss-scss "^2.0.0" + postcss-selector-parser "^6.0.2" + postcss-syntax "^0.36.2" + postcss-value-parser "^4.0.2" + resolve-from "^5.0.0" + slash "^3.0.0" + specificity "^0.4.1" + string-width "^4.2.0" + strip-ansi "^6.0.0" + style-search "^0.1.0" + sugarss "^2.0.0" + svg-tags "^1.0.0" + table "^5.4.6" + v8-compile-cache "^2.1.0" + write-file-atomic "^3.0.1" + +sugarss@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/sugarss/-/sugarss-2.0.0.tgz#ddd76e0124b297d40bf3cca31c8b22ecb43bc61d" + integrity sha512-WfxjozUk0UVA4jm+U1d736AUpzSrNsQcIbyOkoE364GrtWmIrFdk5lksEupgWMD4VaT/0kVx1dobpiDumSgmJQ== + dependencies: + postcss "^7.0.2" + +supports-color@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" + integrity sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg== + dependencies: + has-flag "^3.0.0" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= + +supports-color@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" + integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY= + dependencies: + has-flag "^1.0.0" + +supports-color@^5.3.0, supports-color@^5.4.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" -syntax-error@^1.1.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.4.0.tgz#2d9d4ff5c064acb711594a3e3b95054ad51d907c" - integrity sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w== +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== dependencies: - acorn-node "^1.2.0" + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" + integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== + dependencies: + has-flag "^4.0.0" + +svg-tags@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" + integrity sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q= + +svgo@^1.0.0, svgo@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" + integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw== + dependencies: + chalk "^2.4.1" + coa "^2.0.2" + css-select "^2.0.0" + css-select-base-adapter "^0.1.1" + css-tree "1.0.0-alpha.37" + csso "^4.0.2" + js-yaml "^3.13.1" + mkdirp "~0.5.1" + object.values "^1.1.0" + sax "~1.2.4" + stable "^0.1.8" + unquote "~1.1.1" + util.promisify "~1.0.0" + +symbol-tree@^3.2.2: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + +table@^5.2.3, table@^5.4.6: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" tar-fs@^2.0.0: version "2.0.0" @@ -2514,7 +6623,7 @@ tar-fs@^2.0.0: pump "^3.0.0" tar-stream "^2.0.0" -tar-stream@^2.0.0, tar-stream@^2.1.0: +tar-stream@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.0.tgz#d1aaa3661f05b38b5acc9b7020efdca5179a2cc3" integrity sha512-+DAn4Nb4+gz6WZigRzKEZl1QuJVOLtAwwF+WUxy1fJ6X63CaGaUAxJRD2KEn1OMfcbCjySTYpNC6WmfQoIEOdw== @@ -2525,14 +6634,42 @@ tar-stream@^2.0.0, tar-stream@^2.1.0: inherits "^2.0.3" readable-stream "^3.1.1" -term-size@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" - integrity sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk= +tar@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.1.tgz#7b3bd6c313cb6e0153770108f8d70ac298607efa" + integrity sha512-bKhKrrz2FJJj5s7wynxy/fyxpE0CmCjmOQ1KV4KkgXFWOgoIT/NbTMnB1n+LFNrNk0SSBVGGxcK5AGsyC+pW5Q== dependencies: - execa "^0.7.0" + chownr "^1.1.3" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.0" + mkdirp "^1.0.3" + yallist "^4.0.0" -through2@^2.0.0: +terser@^3.7.3: + version "3.17.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-3.17.0.tgz#f88ffbeda0deb5637f9d24b0da66f4e15ab10cb2" + integrity sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ== + dependencies: + commander "^2.19.0" + source-map "~0.6.1" + source-map-support "~0.5.10" + +terser@^4.3.9: + version "4.6.3" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.3.tgz#e33aa42461ced5238d352d2df2a67f21921f8d87" + integrity sha512-Lw+ieAXmY69d09IIc/yqeBqXpEQIpDGZqT34ui1QWXIUpR2RjbqEkT8X7Lgex19hslSqcWM5iMN2kM11eMsESQ== + dependencies: + commander "^2.20.0" + source-map "~0.6.1" + source-map-support "~0.5.12" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +through2@^2.0.0, through2@~2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== @@ -2540,22 +6677,49 @@ through2@^2.0.0: readable-stream "~2.3.6" xtend "~4.0.1" -"through@>=2.2.7 <3": +through@^2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= -timed-out@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" - integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= - -timers-browserify@^1.0.1: - version "1.4.2" - resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d" - integrity sha1-ycWLV1voQHN1y14kYtrO50NZ9B0= +timers-browserify@^2.0.4: + version "2.0.11" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.11.tgz#800b1f3eee272e5bc53ee465a04d0e804c31211f" + integrity sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ== dependencies: - process "~0.11.0" + setimmediate "^1.0.4" + +timsort@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" + integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= + +tiny-inflate@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4" + integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw== + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +to-arraybuffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= + +to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc= + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= to-object-path@^0.3.0: version "0.3.0" @@ -2572,6 +6736,13 @@ to-regex-range@^2.1.0: is-number "^3.0.0" repeat-string "^1.6.1" +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" @@ -2582,61 +6753,187 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" -touch@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" - integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA== +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +tough-cookie@^2.3.3, tough-cookie@^2.5.0, tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== dependencies: - nopt "~1.0.10" + psl "^1.1.28" + punycode "^2.1.1" + +tr46@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk= + dependencies: + punycode "^2.1.0" + +trim-newlines@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.0.tgz#79726304a6a898aa8373427298d54c2ee8b1cb30" + integrity sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA== + +trim-trailing-lines@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.3.tgz#7f0739881ff76657b7776e10874128004b625a94" + integrity sha512-4ku0mmjXifQcTVfYDfR5lpgV7zVqPg6zV9rdZmwOPqq0+Zq19xDqEgagqVbc4pOOShbncuAOIs59R3+3gcF3ZA== + +trim@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" + integrity sha1-WFhUf2spB1fulczMZm+1AITEYN0= + +trough@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" + integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== ts-node@^8.4.1: - version "8.5.4" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.5.4.tgz#a152add11fa19c221d0b48962c210cf467262ab2" - integrity sha512-izbVCRV68EasEPQ8MSIGBNK9dc/4sYJJKYA+IarMQct1RtEot6Xp0bXuClsbUSnKpg50ho+aOAx8en5c+y4OFw== + version "8.6.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.6.2.tgz#7419a01391a818fbafa6f826a33c1a13e9464e35" + integrity sha512-4mZEbofxGqLL2RImpe3zMJukvEvcO1XP8bj8ozBPySdCUXEcU5cIRwR0aM3R+VoZq7iXc8N86NC0FspGRqP4gg== dependencies: arg "^4.1.0" diff "^4.0.1" make-error "^1.1.1" source-map-support "^0.5.6" - yn "^3.0.0" + yn "3.1.1" -tty-browserify@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811" - integrity sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw== +tslib@^1.8.1, tslib@^1.9.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" + integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== + +tsutils@^3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" + integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g== + dependencies: + tslib "^1.8.1" + +tty-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@3.6: - version "3.6.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.4.tgz#b18752bb3792bc1a0281335f7f6ebf1bbfc5b91d" - integrity sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg== +typescript@3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb" + integrity sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ== -umd@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.3.tgz#aa9fe653c42b9097678489c01000acb69f0b26cf" - integrity sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow== +uncss@^0.17.2: + version "0.17.3" + resolved "https://registry.yarnpkg.com/uncss/-/uncss-0.17.3.tgz#50fc1eb4ed573ffff763458d801cd86e4d69ea11" + integrity sha512-ksdDWl81YWvF/X14fOSw4iu8tESDHFIeyKIeDrK6GEVTQvqJc1WlOEXqostNwOCi3qAj++4EaLsdAgPmUbEyog== + dependencies: + commander "^2.20.0" + glob "^7.1.4" + is-absolute-url "^3.0.1" + is-html "^1.1.0" + jsdom "^14.1.0" + lodash "^4.17.15" + postcss "^7.0.17" + postcss-selector-parser "6.0.2" + request "^2.88.0" -undeclared-identifiers@^1.1.2: +unherit@^1.0.4: version "1.1.3" - resolved "https://registry.yarnpkg.com/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz#9254c1d37bdac0ac2b52de4b6722792d2a91e30f" - integrity sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw== + resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.3.tgz#6c9b503f2b41b262330c80e91c8614abdaa69c22" + integrity sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ== dependencies: - acorn-node "^1.3.0" - dash-ast "^1.0.0" - get-assigned-identifiers "^1.2.0" - simple-concat "^1.0.0" - xtend "^4.0.1" + inherits "^2.0.0" + xtend "^4.0.0" -undefsafe@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.2.tgz#225f6b9e0337663e0d8e7cfd686fc2836ccace76" - integrity sha1-Il9rngM3Zj4Njnz9aG/Cg2zKznY= +unicode-canonical-property-names-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" + integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== + +unicode-match-property-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" + integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== dependencies: - debug "^2.2.0" + unicode-canonical-property-names-ecmascript "^1.0.4" + unicode-property-aliases-ecmascript "^1.0.4" + +unicode-match-property-value-ecmascript@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz#5b4b426e08d13a80365e0d657ac7a6c1ec46a277" + integrity sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g== + +unicode-property-aliases-ecmascript@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz#a9cc6cc7ce63a0a3023fc99e341b94431d405a57" + integrity sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw== + +unicode-trie@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/unicode-trie/-/unicode-trie-0.3.1.tgz#d671dddd89101a08bac37b6a5161010602052085" + integrity sha1-1nHd3YkQGgi6w3tqUWEBBgIFIIU= + dependencies: + pako "^0.2.5" + tiny-inflate "^1.0.0" + +unified@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/unified/-/unified-7.1.0.tgz#5032f1c1ee3364bd09da12e27fdd4a7553c7be13" + integrity sha512-lbk82UOIGuCEsZhPj8rNAkXSDXd6p0QLzIuSsCdxrqnqU56St4eyOB+AlXsVgVeRmetPTYydIuvFfpDIed8mqw== + dependencies: + "@types/unist" "^2.0.0" + "@types/vfile" "^3.0.0" + bail "^1.0.0" + extend "^3.0.0" + is-plain-obj "^1.1.0" + trough "^1.0.0" + vfile "^3.0.0" + x-is-string "^0.1.0" union-value@^1.0.0: version "1.0.1" @@ -2648,18 +6945,71 @@ union-value@^1.0.0: is-extendable "^0.1.1" set-value "^2.0.1" -unique-string@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-1.0.0.tgz#9e1057cca851abb93398f8b33ae187b99caec11a" - integrity sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo= +uniq@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" + integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= + +uniqs@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" + integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI= + +unist-util-find-all-after@^1.0.2: + version "1.0.5" + resolved "https://registry.yarnpkg.com/unist-util-find-all-after/-/unist-util-find-all-after-1.0.5.tgz#5751a8608834f41d117ad9c577770c5f2f1b2899" + integrity sha512-lWgIc3rrTMTlK1Y0hEuL+k+ApzFk78h+lsaa2gHf63Gp5Ww+mt11huDniuaoq1H+XMK2lIIjjPkncxXcDp3QDw== dependencies: - crypto-random-string "^1.0.0" + unist-util-is "^3.0.0" + +unist-util-is@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-3.0.0.tgz#d9e84381c2468e82629e4a5be9d7d05a2dd324cd" + integrity sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A== + +unist-util-remove-position@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-1.1.4.tgz#ec037348b6102c897703eee6d0294ca4755a2020" + integrity sha512-tLqd653ArxJIPnKII6LMZwH+mb5q+n/GtXQZo6S6csPRs5zB0u79Yw8ouR3wTw8wxvdJFhpP6Y7jorWdCgLO0A== + dependencies: + unist-util-visit "^1.1.0" + +unist-util-stringify-position@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz#3f37fcf351279dcbca7480ab5889bb8a832ee1c6" + integrity sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ== + +unist-util-stringify-position@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-2.0.2.tgz#5a3866e7138d55974b640ec69a94bc19e0f3fa12" + integrity sha512-nK5n8OGhZ7ZgUwoUbL8uiVRwAbZyzBsB/Ddrlbu6jwwubFza4oe15KlyEaLNMXQW1svOQq4xesUeqA85YrIUQA== + dependencies: + "@types/unist" "^2.0.2" + +unist-util-visit-parents@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz#25e43e55312166f3348cae6743588781d112c1e9" + integrity sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g== + dependencies: + unist-util-is "^3.0.0" + +unist-util-visit@^1.1.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-1.4.1.tgz#4724aaa8486e6ee6e26d7ff3c8685960d560b1e3" + integrity sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw== + dependencies: + unist-util-visit-parents "^2.0.0" universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +unquote@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" + integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ= + unset-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" @@ -2668,45 +7018,24 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" -unzip-response@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" - integrity sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c= - upath@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== -update-notifier@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.5.0.tgz#d0744593e13f161e406acb1d9408b72cad08aff6" - integrity sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw== +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== dependencies: - boxen "^1.2.1" - chalk "^2.0.1" - configstore "^3.0.0" - import-lazy "^2.1.0" - is-ci "^1.0.10" - is-installed-globally "^0.1.0" - is-npm "^1.0.0" - latest-version "^3.0.0" - semver-diff "^2.0.0" - xdg-basedir "^3.0.0" + punycode "^2.1.0" urix@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= -url-parse-lax@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" - integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= - dependencies: - prepend-http "^1.0.1" - -url@~0.11.0: +url@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= @@ -2724,6 +7053,16 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= +util.promisify@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" + integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.2" + has-symbols "^1.0.1" + object.getownpropertydescriptors "^2.1.0" + util@0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" @@ -2731,29 +7070,94 @@ util@0.10.3: dependencies: inherits "2.0.1" -util@^0.12.1: - version "0.12.1" - resolved "https://registry.yarnpkg.com/util/-/util-0.12.1.tgz#f908e7b633e7396c764e694dd14e716256ce8ade" - integrity sha512-MREAtYOp+GTt9/+kwf00IYoHZyjM8VU4aVrkzUlejyqaIjd2GztVl5V9hGXKlvBKE3gENn/FMfHE5v6hElXGcQ== - dependencies: - inherits "^2.0.3" - is-arguments "^1.0.4" - is-generator-function "^1.0.7" - object.entries "^1.1.0" - safe-buffer "^5.1.2" - -util@~0.10.1: - version "0.10.4" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" - integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== +util@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" + integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== dependencies: inherits "2.0.3" -vm-browserify@^1.0.0: +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +v8-compile-cache@^2.0.0, v8-compile-cache@^2.0.3, v8-compile-cache@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" + integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +vendors@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e" + integrity sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w== + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vfile-location@^2.0.0: + version "2.0.6" + resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-2.0.6.tgz#8a274f39411b8719ea5728802e10d9e0dff1519e" + integrity sha512-sSFdyCP3G6Ka0CEmN83A2YCMKIieHx0EDaj5IDP4g1pa5ZJ4FJDvpO0WODLxo4LUX4oe52gmSCK7Jw4SBghqxA== + +vfile-message@*, vfile-message@^1.0.0, vfile-message@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.2.tgz#75ba05090ec758fa8420f2c11ce049bcddd8cf3e" + integrity sha512-gNV2Y2fDvDOOqq8bEe7cF3DXU6QgV4uA9zMR2P8tix11l1r7zju3zry3wZ8sx+BEfuO6WQ7z2QzfWTvqHQiwsA== + dependencies: + "@types/unist" "^2.0.0" + unist-util-stringify-position "^2.0.0" + +vfile@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-3.0.1.tgz#47331d2abe3282424f4a4bb6acd20a44c4121803" + integrity sha512-y7Y3gH9BsUSdD4KzHsuMaCzRjglXN0W2EcMf0gpvu6+SbsGhMje7xDc8AEoeXy6mIwCKMI6BkjMsRjzQbhMEjQ== + dependencies: + is-buffer "^2.0.0" + replace-ext "1.0.0" + unist-util-stringify-position "^1.0.0" + vfile-message "^1.0.0" + +vlq@^0.2.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.3.tgz#8f3e4328cf63b1540c0d67e1b2778386f8975b26" + integrity sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow== + +vm-browserify@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== +w3c-hr-time@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045" + integrity sha1-gqwr/2PZUOqeMYmlimViX+3xkEU= + dependencies: + browser-process-hrtime "^0.1.2" + +w3c-xmlserializer@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz#30485ca7d70a6fd052420a3d12fd90e6339ce794" + integrity sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg== + dependencies: + domexception "^1.0.1" + webidl-conversions "^4.0.2" + xml-name-validator "^3.0.0" + wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" @@ -2761,50 +7165,222 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" -which@^1.2.9, which@^1.3.1: +weakmap-shim@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/weakmap-shim/-/weakmap-shim-1.1.1.tgz#d65afd784109b2166e00ff571c33150ec2a40b49" + integrity sha1-1lr9eEEJshZuAP9XHDMVDsKkC0k= + +webidl-conversions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" + integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== + +whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" + integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== + dependencies: + iconv-lite "0.4.24" + +whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" + integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== + +whatwg-url@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" + integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which@1.3.1, which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" -widest-line@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.1.tgz#7438764730ec7ef4381ce4df82fb98a53142a3fc" - integrity sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA== +which@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: - string-width "^2.1.1" + isexe "^2.0.0" + +wide-align@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -write-file-atomic@^2.0.0: - version "2.4.3" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" - integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== +write-file-atomic@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.1.tgz#558328352e673b5bb192cf86500d60b230667d4b" + integrity sha512-JPStrIyyVJ6oCSz/691fAjFtefZ6q+fP6tm+OS4Qw6o+TGQxNp1ziY2PgS+X/m0V8OWhZiO/m4xSj+Pr4RrZvw== dependencies: - graceful-fs "^4.1.11" imurmurhash "^0.1.4" + is-typedarray "^1.0.0" signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" -xdg-basedir@^3.0.0: +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + +ws@^5.1.1: + version "5.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" + integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA== + dependencies: + async-limiter "~1.0.0" + +ws@^6.1.2: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" + integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== + dependencies: + async-limiter "~1.0.0" + +ws@^7.2.0: + version "7.2.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.1.tgz#03ed52423cd744084b2cf42ed197c8b65a936b8e" + integrity sha512-sucePNSafamSKoOqoNfBd8V0StlkzJKL2ZAhGQinCfNQ+oacw+Pk7lcdAElecBF2VkLNZRiIb5Oi1Q5lVUVt2A== + +x-is-string@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/x-is-string/-/x-is-string-0.1.0.tgz#474b50865af3a49a9c4657f05acd145458f77d82" + integrity sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI= + +xml-name-validator@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" - integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ= + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" + integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== -xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.1: +xmlchars@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + +xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= +y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== -yn@^3.0.0: +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@^1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.7.2.tgz#f26aabf738590ab61efaca502358e48dc9f348b2" + integrity sha512-qXROVp90sb83XtAoqE8bP9RwAkTTZbugRUTm5YeFCBfNRPEp2YzTeqWiz7m5OORHzEvrA/qcGS8hp/E+MMROYw== + dependencies: + "@babel/runtime" "^7.6.3" + +yargs-parser@13.1.1, yargs-parser@^13.1.1: + version "13.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" + integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^15.0.0: + version "15.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-15.0.0.tgz#cdd7a97490ec836195f59f3f4dbe5ea9e8f75f08" + integrity sha512-xLTUnCMc4JhxrPEPUYD5IBR1mWCK/aT6+RJ/K29JY2y1vD+FhtgKK0AXRWvI262q3QSffAQuTouFIKUuHX89wQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^16.1.0: + version "16.1.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-16.1.0.tgz#73747d53ae187e7b8dbe333f95714c76ea00ecf1" + integrity sha512-H/V41UNZQPkUMIT5h5hiwg4QKIY1RPvoBV4XcjUbRM8Bk2oKqqyZ0DIEbTFZB0XjbtSPG8SAa/0DxCQmiRgzKg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-unparser@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f" + integrity sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw== + dependencies: + flat "^4.1.0" + lodash "^4.17.15" + yargs "^13.3.0" + +yargs@13.3.0, yargs@^13.3.0: + version "13.3.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" + integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA== + dependencies: + cliui "^5.0.0" + find-up "^3.0.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 "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.1" + +yargs@^14.0.0: + version "14.2.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.2.tgz#2769564379009ff8597cdd38fba09da9b493c4b5" + integrity sha512-/4ld+4VV5RnrynMhPZJ/ZpOCGSCeghMykZ3BhdFBDa9Wy/RH6uEGNWDJog+aUlq+9OM1CFTgtYRW5Is1Po9NOA== + dependencies: + cliui "^5.0.0" + decamelize "^1.2.0" + find-up "^3.0.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 "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^15.0.0" + +yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==