diff --git a/.github/regsync.yml b/.github/regsync.yml new file mode 100644 index 000000000..56d5f176e --- /dev/null +++ b/.github/regsync.yml @@ -0,0 +1,52 @@ + version: 1 + creds: + - registry: docker.io + user: "{{env \"DOCKERHUB_USERNAME\"}}" + pass: "{{env \"DOCKERHUB_TOKEN\"}}" + + defaults: + ratelimit: + min: 100 + retry: 1m + parallel: 4 + + sync: + + - source: ghcr.io/syncthing/syncthing + target: docker.io/syncthing/syncthing + type: repository + tags: + allow: + - latest + - rc + - edge + - \d+ + - \d+\.\d+ + - \d+\.\d+\.\d+ + - \d+\.\d+\.\d+-rc\.\d+ + + - source: ghcr.io/syncthing/relaysrv + target: docker.io/syncthing/relaysrv + type: repository + tags: + allow: + - latest + - rc + - edge + - \d+ + - \d+\.\d+ + - \d+\.\d+\.\d+ + - \d+\.\d+\.\d+-rc\.\d+ + + - source: ghcr.io/syncthing/discosrv + target: docker.io/syncthing/discosrv + type: repository + tags: + allow: + - latest + - rc + - edge + - \d+ + - \d+\.\d+ + - \d+\.\d+\.\d+ + - \d+\.\d+\.\d+-rc\.\d+ diff --git a/.github/workflows/build-syncthing.yaml b/.github/workflows/build-syncthing.yaml index 64699f3d0..1d473ccd1 100644 --- a/.github/workflows/build-syncthing.yaml +++ b/.github/workflows/build-syncthing.yaml @@ -16,9 +16,8 @@ env: GO_VERSION: "~1.24.0" # Optimize compatibility on the slow archictures. - GO386: softfloat - GOARM: "5" GOMIPS: softfloat + GOARM: "6" # Avoid hilarious amounts of obscuring log output when running tests. LOGGER_DISCARD: "1" @@ -27,6 +26,8 @@ env: BUILD_USER: builder BUILD_HOST: github.syncthing.net + TAGS: "netgo osusergo sqlite_omit_load_extension sqlite_dbstat" + # A note on actions and third party code... The actions under actions/ (like # `uses: actions/checkout`) are maintained by GitHub, and we need to trust # GitHub to maintain their code and infrastructure or we're in deep shit in @@ -36,6 +37,51 @@ env: jobs: + # + # Source + # + + facts: + name: Gather common facts + runs-on: ubuntu-latest + outputs: + version: ${{ steps.get-version.outputs.version }} + release-kind: ${{ steps.get-version.outputs.release-kind }} + go-version: ${{ steps.get-go.outputs.go-version }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882 + + - uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + cache: false + check-latest: true + + - name: Get Syncthing version + id: get-version + run: | + version=$(go run build.go version) + echo "version=$version" >> "$GITHUB_OUTPUT" + echo "Version: $version" + + kind=stable + if [[ $version == *-rc.[0-9] || $version == *-rc.[0-9][0-9] ]] ; then + kind=candidate + elif [[ $version == *-* ]] ; then + kind=nightly + fi + echo "release-kind=$kind" >> "$GITHUB_OUTPUT" + echo "Release kind: $kind" + + - name: Get Go version + id: get-go + run: | + go version + echo "go-version=$(go version | sed 's#^.*go##;s# .*##')" >> $GITHUB_OUTPUT + # # Tests for all platforms. Runs a matrix build on Windows, Linux and Mac, # with the list of expected supported Go versions (current, previous). @@ -88,6 +134,7 @@ jobs: LOKI_USER: ${{ secrets.LOKI_USER }} LOKI_PASSWORD: ${{ secrets.LOKI_PASSWORD }} LOKI_LABELS: "go=${{ matrix.go }},runner=${{ matrix.runner }},repo=${{ github.repository }},ref=${{ github.ref }}" + CGO_ENABLED: "1" # # The basic checks job is a virtual one that depends on the matrix tests, @@ -109,6 +156,8 @@ jobs: - package-debian - package-windows - govulncheck + - golangci + - meta steps: - uses: actions/checkout@v4 @@ -118,39 +167,28 @@ jobs: package-windows: name: Package for Windows - runs-on: windows-latest + runs-on: ubuntu-latest + needs: + - facts + env: + VERSION: ${{ needs.facts.outputs.version }} + RELEASE_KIND: ${{ needs.facts.outputs.release-kind }} steps: - - name: Set git to use LF - # Without this, the checkout will happen with CRLF line endings, - # which is fine for the source code but messes up tests that depend - # on data on disk being as expected. Ideally, those tests should be - # fixed, but not today. - run: | - git config --global core.autocrlf false - git config --global core.eol lf - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882 - uses: actions/setup-go@v5 with: - go-version: ${{ env.GO_VERSION }} + go-version: ${{ needs.facts.outputs.go-version }} cache: false - check-latest: true - - name: Get actual Go version - run: | - go version - echo "GO_VERSION=$(go version | sed 's#^.*go##;s# .*##')" >> $GITHUB_ENV + - uses: mlugg/setup-zig@v2 - uses: actions/cache@v4 with: path: | - ~\AppData\Local\go-build - ~\go\pkg\mod - key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-package-${{ hashFiles('**/go.sum') }} + ~/.cache/go-build + ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ needs.facts.outputs.go-version }}-package-windows-${{ hashFiles('**/go.sum') }} - name: Install dependencies run: | @@ -158,15 +196,14 @@ jobs: - name: Create packages run: | - $targets = 'syncthing', 'stdiscosrv', 'strelaysrv' - $archs = 'amd64', 'arm', 'arm64', '386' - foreach ($arch in $archs) { - foreach ($tgt in $targets) { - go run build.go -goarch $arch zip $tgt - } - } + for tgt in syncthing stdiscosrv strelaysrv ; do + go run build.go -tags "${{env.TAGS}}" -goos windows -goarch amd64 -cc "zig cc -target x86_64-windows" zip $tgt + go run build.go -tags "${{env.TAGS}}" -goos windows -goarch 386 -cc "zig cc -target x86-windows" zip $tgt + go run build.go -tags "${{env.TAGS}}" -goos windows -goarch arm64 -cc "zig cc -target aarch64-windows" zip $tgt + # go run build.go -tags "${{env.TAGS}}" -goos windows -goarch arm -cc "zig cc -target thumb-windows" zip $tgt # failes with linker errors + done env: - CGO_ENABLED: "0" + CGO_ENABLED: "1" - name: Archive artifacts uses: actions/upload-artifact@v4 @@ -174,9 +211,15 @@ jobs: name: unsigned-packages-windows path: "*.zip" + # + # Codesign binaries for Windows. This job runs only when called in the + # Syncthing repo for release branches and tags, as it requires our + # specific code signing keys etc. + # + codesign-windows: name: Codesign for Windows - if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v')) + if: github.repository_owner == 'syncthing' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v')) environment: release runs-on: windows-latest needs: @@ -234,40 +277,49 @@ jobs: package-linux: name: Package for Linux runs-on: ubuntu-latest + needs: + - facts + env: + VERSION: ${{ needs.facts.outputs.version }} + RELEASE_KIND: ${{ needs.facts.outputs.release-kind }} steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882 - uses: actions/setup-go@v5 with: - go-version: ${{ env.GO_VERSION }} + go-version: ${{ needs.facts.outputs.go-version }} cache: false - check-latest: true - - name: Get actual Go version - run: | - go version - echo "GO_VERSION=$(go version | sed 's#^.*go##;s# .*##')" >> $GITHUB_ENV + - uses: mlugg/setup-zig@v2 - uses: actions/cache@v4 with: path: | ~/.cache/go-build ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-package-${{ hashFiles('**/go.sum') }} + key: ${{ runner.os }}-go-${{ needs.facts.outputs.go-version }}-package-${{ hashFiles('**/go.sum') }} - name: Create packages run: | - archs=$(go tool dist list | grep linux | sed 's#linux/##') - for goarch in $archs ; do - for tgt in syncthing stdiscosrv strelaysrv ; do - go run build.go -goarch "$goarch" tar "$tgt" - done + sudo apt-get install -y gcc-mips64-linux-gnuabi64 gcc-mips64el-linux-gnuabi64 + for tgt in syncthing stdiscosrv strelaysrv ; do + go run build.go -tags "${{env.TAGS}}" -goos linux -goarch amd64 -cc "zig cc -target x86_64-linux-musl" tar "$tgt" + go run build.go -tags "${{env.TAGS}}" -goos linux -goarch 386 -cc "zig cc -target x86-linux-musl" tar "$tgt" + go run build.go -tags "${{env.TAGS}}" -goos linux -goarch arm -cc "zig cc -target arm-linux-musleabi -mcpu=arm1136j_s" tar "$tgt" + go run build.go -tags "${{env.TAGS}}" -goos linux -goarch arm64 -cc "zig cc -target aarch64-linux-musl" tar "$tgt" + go run build.go -tags "${{env.TAGS}}" -goos linux -goarch mips -cc "zig cc -target mips-linux-musleabi" tar "$tgt" + go run build.go -tags "${{env.TAGS}}" -goos linux -goarch mipsle -cc "zig cc -target mipsel-linux-musleabi" tar "$tgt" + go run build.go -tags "${{env.TAGS}}" -goos linux -goarch mips64 -cc mips64-linux-gnuabi64-gcc tar "$tgt" + go run build.go -tags "${{env.TAGS}}" -goos linux -goarch mips64le -cc mips64el-linux-gnuabi64-gcc tar "$tgt" + go run build.go -tags "${{env.TAGS}}" -goos linux -goarch riscv64 -cc "zig cc -target riscv64-linux-musl" tar "$tgt" + go run build.go -tags "${{env.TAGS}}" -goos linux -goarch s390x -cc "zig cc -target s390x-linux-musl" tar "$tgt" + go run build.go -tags "${{env.TAGS}}" -goos linux -goarch loong64 -cc "zig cc -target loongarch64-linux-musl" tar "$tgt" + # go run build.go -tags "${{env.TAGS}}" -goos linux -goarch ppc64 -cc "zig cc -target powerpc64-linux-musl" tar "$tgt" # fails with linkmode not supported + go run build.go -tags "${{env.TAGS}}" -goos linux -goarch ppc64le -cc "zig cc -target powerpc64le-linux-musl" tar "$tgt" done env: - CGO_ENABLED: "0" + CGO_ENABLED: "1" + EXTRA_LDFLAGS: "-linkmode=external -extldflags=-static" - name: Archive artifacts uses: actions/upload-artifact@v4 @@ -278,39 +330,39 @@ jobs: compat.json # - # macOS + # macOS. The entire build runs in the release environment because code + # signing is part of the build process, so it is limited to release + # branches on the Syncthing repo. # package-macos: name: Package for macOS - if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v')) + if: github.repository_owner == 'syncthing' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v')) environment: release runs-on: macos-latest + needs: + - facts + env: + CODESIGN_IDENTITY: ${{ secrets.CODESIGN_IDENTITY }} + VERSION: ${{ needs.facts.outputs.version }} + RELEASE_KIND: ${{ needs.facts.outputs.release-kind }} steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882 - uses: actions/setup-go@v5 with: - go-version: ${{ env.GO_VERSION }} + go-version: ${{ needs.facts.outputs.go-version }} cache: false - check-latest: true - - - name: Get actual Go version - run: | - go version - echo "GO_VERSION=$(go version | sed 's#^.*go##;s# .*##')" >> $GITHUB_ENV - uses: actions/cache@v4 with: path: | ~/.cache/go-build ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-package-${{ hashFiles('**/go.sum') }} + key: ${{ runner.os }}-go-${{ needs.facts.outputs.go-version }}-package-${{ hashFiles('**/go.sum') }} - name: Import signing certificate + if: env.CODESIGN_IDENTITY != '' run: | # Set up a run-specific keychain, making it available for the # `codesign` tool. @@ -338,7 +390,7 @@ jobs: - name: Create package (amd64) run: | for tgt in syncthing stdiscosrv strelaysrv ; do - go run build.go -goarch amd64 zip "$tgt" + go run build.go -tags "${{env.TAGS}}" -goarch amd64 zip "$tgt" done env: CGO_ENABLED: "1" @@ -354,7 +406,7 @@ jobs: EOT chmod 755 xgo.sh for tgt in syncthing stdiscosrv strelaysrv ; do - go run build.go -gocmd ./xgo.sh -goarch arm64 zip "$tgt" + go run build.go -tags "${{env.TAGS}}" -gocmd ./xgo.sh -goarch arm64 zip "$tgt" done env: CGO_ENABLED: "1" @@ -383,7 +435,7 @@ jobs: notarize-macos: name: Notarize for macOS - if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v')) + if: github.repository_owner == 'syncthing' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v')) environment: release needs: - package-macos @@ -417,29 +469,25 @@ jobs: package-cross: name: Package cross compiled runs-on: ubuntu-latest + needs: + - facts + env: + VERSION: ${{ needs.facts.outputs.version }} + RELEASE_KIND: ${{ needs.facts.outputs.release-kind }} steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882 - uses: actions/setup-go@v5 with: - go-version: ${{ env.GO_VERSION }} + go-version: ${{ needs.facts.outputs.go-version }} cache: false - check-latest: true - - - name: Get actual Go version - run: | - go version - echo "GO_VERSION=$(go version | sed 's#^.*go##;s# .*##')" >> $GITHUB_ENV - uses: actions/cache@v4 with: path: | ~/.cache/go-build ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-cross-${{ hashFiles('**/go.sum') }} + key: ${{ runner.os }}-go-${{ needs.facts.outputs.go-version }}-cross-${{ hashFiles('**/go.sum') }} - name: Create packages run: | @@ -465,7 +513,7 @@ jobs: goarch="${plat#*/}" echo "::group ::$plat" for tgt in syncthing stdiscosrv strelaysrv ; do - if ! go run build.go -goos "$goos" -goarch "$goarch" tar "$tgt" 2>/dev/null; then + if ! go run build.go -goos "$goos" -goarch "$goarch" tar "$tgt" ; then echo "::warning ::Failed to build $tgt for $plat" fi done @@ -487,33 +535,33 @@ jobs: package-source: name: Package source code runs-on: ubuntu-latest + needs: + - facts + env: + VERSION: ${{ needs.facts.outputs.version }} + RELEASE_KIND: ${{ needs.facts.outputs.release-kind }} steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882 - uses: actions/setup-go@v5 with: - go-version: ${{ env.GO_VERSION }} + go-version: ${{ needs.facts.outputs.go-version }} cache: false - check-latest: true - name: Package source run: | - version=$(go run build.go version) - echo "$version" > RELEASE + echo "$VERSION" > RELEASE go mod vendor go run build.go assets cd .. - tar c -z -f "syncthing-source-$version.tar.gz" \ + tar c -z -f "syncthing-source-$VERSION.tar.gz" \ --exclude .git \ syncthing - mv "syncthing-source-$version.tar.gz" syncthing + mv "syncthing-source-$VERSION.tar.gz" syncthing - name: Archive artifacts uses: actions/upload-artifact@v4 @@ -527,7 +575,7 @@ jobs: sign-for-upgrade: name: Sign for upgrade - if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v')) + if: github.repository_owner == 'syncthing' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v')) environment: release needs: - codesign-windows @@ -535,27 +583,26 @@ jobs: - package-macos - package-cross - package-source + - facts + env: + VERSION: ${{ needs.facts.outputs.version }} + RELEASE_KIND: ${{ needs.facts.outputs.release-kind }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882 - uses: actions/checkout@v4 with: repository: syncthing/release-tools path: tools - fetch-depth: 0 - name: Download artifacts uses: actions/download-artifact@v4 - uses: actions/setup-go@v5 with: - go-version: ${{ env.GO_VERSION }} + go-version: ${{ needs.facts.outputs.go-version }} cache: false - check-latest: true - name: Install signing tool run: | @@ -582,9 +629,6 @@ jobs: sha256sum "${files[@]}" > sha256sum.txt popd - version=$(go run build.go version) - echo "VERSION=$version" >> $GITHUB_ENV - - name: Sign shasum files uses: docker://ghcr.io/kastelo/ezapt:latest with: @@ -620,22 +664,18 @@ jobs: package-debian: name: Package for Debian runs-on: ubuntu-latest + needs: + - facts + env: + VERSION: ${{ needs.facts.outputs.version }} + RELEASE_KIND: ${{ needs.facts.outputs.release-kind }} steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882 - uses: actions/setup-go@v5 with: - go-version: ${{ env.GO_VERSION }} + go-version: ${{ needs.facts.outputs.go-version }} cache: false - check-latest: true - - - name: Get actual Go version - run: | - go version - echo "GO_VERSION=$(go version | sed 's#^.*go##;s# .*##')" >> $GITHUB_ENV - uses: ruby/setup-ruby@v1 with: @@ -645,22 +685,26 @@ jobs: run: | gem install fpm + - uses: mlugg/setup-zig@v2 + - uses: actions/cache@v4 with: path: | ~/.cache/go-build ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-debian-${{ hashFiles('**/go.sum') }} + key: ${{ runner.os }}-go-${{ needs.facts.outputs.go-version }}-debian-${{ hashFiles('**/go.sum') }} - - name: Package for Debian + - name: Package for Debian (CGO) run: | - for arch in amd64 i386 armhf armel arm64 ; do - for tgt in syncthing stdiscosrv strelaysrv ; do - go run build.go -no-upgrade -installsuffix=no-upgrade -goarch "$arch" deb "$tgt" - done + for tgt in syncthing stdiscosrv strelaysrv ; do + go run build.go -no-upgrade -installsuffix=no-upgrade -tags "${{env.TAGS}}" -goos linux -goarch amd64 -cc "zig cc -target x86_64-linux-musl" deb "$tgt" + go run build.go -no-upgrade -installsuffix=no-upgrade -tags "${{env.TAGS}}" -goos linux -goarch arm -cc "zig cc -target arm-linux-musleabi -mcpu=arm1136j_s" deb "$tgt" + go run build.go -no-upgrade -installsuffix=no-upgrade -tags "${{env.TAGS}}" -goos linux -goarch arm64 -cc "zig cc -target aarch64-linux-musl" deb "$tgt" done env: BUILD_USER: debian + CGO_ENABLED: "1" + EXTRA_LDFLAGS: "-linkmode=external -extldflags=-static" - name: Archive artifacts uses: actions/upload-artifact@v4 @@ -674,17 +718,17 @@ jobs: publish-nightly: name: Publish nightly build - if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && startsWith(github.ref, 'refs/heads/release-nightly') + if: github.repository_owner == 'syncthing' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && startsWith(github.ref, 'refs/heads/release-nightly') environment: release needs: - sign-for-upgrade + - facts runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: repository: syncthing/release-tools path: tools - fetch-depth: 0 - name: Download artifacts uses: actions/download-artifact@v4 @@ -694,9 +738,8 @@ jobs: - uses: actions/setup-go@v5 with: - go-version: ${{ env.GO_VERSION }} + go-version: ${{ needs.facts.outputs.go-version }} cache: false - check-latest: true - name: Create release json run: | @@ -724,19 +767,20 @@ jobs: publish-release-files: name: Publish release files - if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/tags/v')) + if: github.repository_owner == 'syncthing' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/tags/v')) environment: release permissions: contents: write needs: - sign-for-upgrade - package-debian + - facts + env: + VERSION: ${{ needs.facts.outputs.version }} + RELEASE_KIND: ${{ needs.facts.outputs.release-kind }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882 - name: Download signed packages uses: actions/download-artifact@v4 @@ -752,14 +796,8 @@ jobs: - uses: actions/setup-go@v5 with: - go-version: ${{ env.GO_VERSION }} + go-version: ${{ needs.facts.outputs.go-version }} cache: false - check-latest: true - - - name: Set version - run: | - version=$(go run build.go version) - echo "VERSION=$version" >> $GITHUB_ENV - name: Push to object store (${{ env.VERSION }}) uses: docker://docker.io/rclone/rclone:latest @@ -829,16 +867,17 @@ jobs: publish-apt: name: Publish APT - if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v')) + if: github.repository_owner == 'syncthing' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v')) environment: release needs: - package-debian + - facts + env: + VERSION: ${{ needs.facts.outputs.version }} + RELEASE_KIND: ${{ needs.facts.outputs.release-kind }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882 - name: Download packages uses: actions/download-artifact@v4 @@ -846,24 +885,11 @@ jobs: name: debian-packages path: packages - - name: Set version - run: | - version=$(go run build.go version) - echo "Version: $version" - echo "VERSION=$version" >> $GITHUB_ENV - # Decide whether packages should go to stable, candidate or nightly - name: Prepare packages run: | - kind=stable - if [[ $VERSION == *-rc.[0-9] ]] ; then - kind=candidate - elif [[ $VERSION == *-* ]] ; then - kind=nightly - fi - echo "Kind: $kind" - mkdir -p packages/syncthing/$kind - mv packages/*.deb packages/syncthing/$kind + mkdir -p packages/syncthing/$RELEASE_KIND + mv packages/*.deb packages/syncthing/$RELEASE_KIND - name: Pull archive uses: docker://docker.io/rclone/rclone:latest @@ -902,17 +928,20 @@ jobs: args: sync -v --no-update-modtime dists objstore:apt/dists # - # Build and push to Docker Hub + # Build and push (except for PRs) to GHCR. # - docker-syncthing: - name: Build and push Docker images + docker-ghcr: + name: Build and push Docker images (GHCR) runs-on: ubuntu-latest - if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/release-nightly' || github.ref == 'refs/heads/infrastructure' || startsWith(github.ref, 'refs/tags/v')) - environment: docker permissions: contents: read packages: write + needs: + - facts + env: + VERSION: ${{ needs.facts.outputs.version }} + RELEASE_KIND: ${{ needs.facts.outputs.release-kind }} strategy: matrix: pkg: @@ -922,64 +951,50 @@ jobs: include: - pkg: syncthing dockerfile: Dockerfile - image: syncthing/syncthing + image: syncthing - pkg: strelaysrv dockerfile: Dockerfile.strelaysrv - image: syncthing/relaysrv + image: relaysrv - pkg: stdiscosrv dockerfile: Dockerfile.stdiscosrv - image: syncthing/discosrv + image: discosrv steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882 - uses: actions/setup-go@v5 with: - go-version: ${{ env.GO_VERSION }} + go-version: ${{ needs.facts.outputs.go-version }} cache: false - check-latest: true - - name: Get actual Go version - run: | - go version - echo "GO_VERSION=$(go version | sed 's#^.*go##;s# .*##')" >> $GITHUB_ENV + - uses: mlugg/setup-zig@v2 - uses: actions/cache@v4 with: path: | ~/.cache/go-build ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-docker-${{ matrix.pkg }}-${{ hashFiles('**/go.sum') }} + key: ${{ runner.os }}-go-${{ needs.facts.outputs.go-version }}-docker-${{ matrix.pkg }}-${{ hashFiles('**/go.sum') }} - - name: Build binaries + - name: Build binaries (CGO) run: | - for arch in amd64 arm64 arm; do - go run build.go -goos linux -goarch "$arch" -no-upgrade build ${{ matrix.pkg }} - mv ${{ matrix.pkg }} ${{ matrix.pkg }}-linux-"$arch" - done + # amd64 + go run build.go -goos linux -goarch amd64 -tags "${{env.TAGS}}" -cc "zig cc -target x86_64-linux-musl" -no-upgrade build ${{ matrix.pkg }} + mv ${{ matrix.pkg }} ${{ matrix.pkg }}-linux-amd64 + + # arm64 + go run build.go -goos linux -goarch arm64 -tags "${{env.TAGS}}" -cc "zig cc -target aarch64-linux-musl" -no-upgrade build ${{ matrix.pkg }} + mv ${{ matrix.pkg }} ${{ matrix.pkg }}-linux-arm64 + + # arm + go run build.go -goos linux -goarch arm -tags "${{env.TAGS}}" -cc "zig cc -target arm-linux-musleabi -mcpu=arm1136j_s" -no-upgrade build ${{ matrix.pkg }} + mv ${{ matrix.pkg }} ${{ matrix.pkg }}-linux-arm env: - CGO_ENABLED: "0" + CGO_ENABLED: "1" BUILD_USER: docker - - - name: Check if we will be able to push images - run: | - if [[ "${{ secrets.DOCKERHUB_TOKEN }}" != "" ]]; then - echo "DOCKER_PUSH=true" >> $GITHUB_ENV; - fi - - - name: Login to Docker Hub - uses: docker/login-action@v3 - if: env.DOCKER_PUSH == 'true' - with: - registry: docker.io - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} + EXTRA_LDFLAGS: "-linkmode=external -extldflags=-static" - name: Login to GHCR uses: docker/login-action@v3 - if: env.DOCKER_PUSH == 'true' with: registry: ghcr.io username: ${{ github.actor }} @@ -990,22 +1005,26 @@ jobs: - name: Set version tags run: | - version=$(go run build.go version) - version=${version#v} + version=${VERSION#v} + repo=ghcr.io/${{ github.repository_owner }}/${{ matrix.image }} + ref="${{github.ref_name}}" + ref=${ref//\//-} # slashes to dashes + + # List of tags for ghcr.io if [[ $version == @([0-9]|[0-9][0-9]).@([0-9]|[0-9][0-9]).@([0-9]|[0-9][0-9]) ]] ; then - echo Release version, pushing to :latest and version tags major=${version%.*.*} minor=${version%.*} - tags=docker.io/${{ matrix.image }}:$version,ghcr.io/${{ matrix.image }}:$version,docker.io/${{ matrix.image }}:$major,ghcr.io/${{ matrix.image }}:$major,docker.io/${{ matrix.image }}:$minor,ghcr.io/${{ matrix.image }}:$minor,docker.io/${{ matrix.image }}:latest,ghcr.io/${{ matrix.image }}:latest + tags=$repo:$version,$repo:$major,$repo:$minor,$repo:latest elif [[ $version == *-rc.@([0-9]|[0-9][0-9]) ]] ; then - echo Release candidate, pushing to :rc and version tags - tags=docker.io/${{ matrix.image }}:$version,ghcr.io/${{ matrix.image }}:$version,docker.io/${{ matrix.image }}:rc,ghcr.io/${{ matrix.image }}:rc + tags=$repo:$version,$repo:rc + elif [[ $ref == "main" ]] ; then + tags=$repo:edge else - echo Development version, pushing to :edge - tags=docker.io/${{ matrix.image }}:edge,ghcr.io/${{ matrix.image }}:edge + tags=$repo:$ref fi + + echo Pushing to $tags echo "DOCKER_TAGS=$tags" >> $GITHUB_ENV - echo "VERSION=$version" >> $GITHUB_ENV - name: Build and push Docker image uses: docker/build-push-action@v5 @@ -1013,12 +1032,36 @@ jobs: context: . file: ${{ matrix.dockerfile }} platforms: linux/amd64,linux/arm64,linux/arm/7 - push: ${{ env.DOCKER_PUSH == 'true' }} tags: ${{ env.DOCKER_TAGS }} + push: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} labels: | org.opencontainers.image.version=${{ env.VERSION }} org.opencontainers.image.revision=${{ github.sha }} + # + # Sync images to Docker hub. This takes the images already pushed to GHCR + # and copies them to Docker hub. Runs for releases only. + # + + docker-hub: + name: Sync images to Docker hub + if: github.repository_owner == 'syncthing' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/release-nightly' || github.ref == 'refs/heads/infrastructure' || startsWith(github.ref, 'refs/tags/v')) + runs-on: ubuntu-latest + needs: + - docker-ghcr + environment: docker + env: + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} + steps: + - uses: actions/checkout@v4 + - name: Sync images + uses: docker://docker.io/regclient/regsync:latest + with: + args: + -c ./.github/regsync.yml + once + # # Check for known vulnerabilities in Go dependencies # @@ -1026,17 +1069,56 @@ jobs: govulncheck: runs-on: ubuntu-latest name: Run govulncheck + needs: + - facts steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: ${{ env.GO_VERSION }} + go-version: ${{ needs.facts.outputs.go-version }} cache: false - check-latest: true - name: run govulncheck run: | go run build.go assets go install golang.org/x/vuln/cmd/govulncheck@latest govulncheck ./... + + # + # golangci-lint runs a suite of static analysis checks on the code + # + + golangci: + runs-on: ubuntu-latest + name: Run golangci-lint + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: 'stable' + + - name: ensure asset generation + run: go run build.go assets + + - name: golangci-lint + uses: golangci/golangci-lint-action@v8 + with: + only-new-issues: true + + # + # Meta checks for formatting, copyright, etc + # + + meta: + name: Run meta checks + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: 'stable' + + - run: | + go run build.go assets + go test -v ./meta diff --git a/.github/workflows/pr-linters.yaml b/.github/workflows/pr-linters.yaml deleted file mode 100644 index 6eda16c17..000000000 --- a/.github/workflows/pr-linters.yaml +++ /dev/null @@ -1,49 +0,0 @@ -name: Run PR linters - -on: - pull_request: - workflow_dispatch: - -permissions: - contents: read - pull-requests: read - -jobs: - - # - # golangci-lint runs a suite of static analysis checks on the code - # - - golangci: - runs-on: ubuntu-latest - name: Golangci-lint - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version: 'stable' - - - name: ensure asset generation - run: go run build.go assets - - - name: golangci-lint - uses: golangci/golangci-lint-action@v8 - with: - only-new-issues: true - - # - # Meta checks for formatting, copyright, etc - # - - meta: - name: Meta checks - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version: 'stable' - - - run: | - go run build.go assets - go test -v ./meta diff --git a/.github/workflows/release-syncthing.yaml b/.github/workflows/release-syncthing.yaml index 59d0fe63d..94e4c83ee 100644 --- a/.github/workflows/release-syncthing.yaml +++ b/.github/workflows/release-syncthing.yaml @@ -19,7 +19,7 @@ jobs: with: fetch-depth: 0 ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882 - token: ${{ secrets.STRELEASE_GITHUB_TOKEN }} + token: ${{ secrets.ACTIONS_GITHUB_TOKEN }} - uses: actions/setup-go@v5 with: @@ -43,7 +43,7 @@ jobs: run: | go run ./script/relnotes.go --new-ver "$NEXT" --branch "$GITHUB_REF_NAME" --prev-ver "$PREV" > notes.md env: - GITHUB_TOKEN: ${{ secrets.STRELEASE_GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.ACTIONS_GITHUB_TOKEN }} - name: Create and push tag run: | diff --git a/.gitignore b/.gitignore index 89eba4edd..27113a847 100644 --- a/.gitignore +++ b/.gitignore @@ -17,5 +17,4 @@ deb *.bz2 /repos /proto/scripts/protoc-gen-gosyncthing -/gui/next-gen-gui /compat.json diff --git a/.golangci.yml b/.golangci.yml index b5b8239ff..153975b35 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -4,15 +4,18 @@ linters: disable: - cyclop - depguard + - err113 - exhaustive - exhaustruct - forbidigo + - funcorder - funlen - gochecknoglobals - gochecknoinits - gocognit - goconst - gocyclo + - godot - godox - gomoddirectives - inamedparam @@ -48,11 +51,13 @@ linters: - std-error-handling paths: - internal/gen + - internal/db/olddb - cmd/dev - repos - third_party$ - builtin$ - examples$ + - _test\.go$ formatters: enable: - gofumpt diff --git a/AUTHORS b/AUTHORS index 106580e58..617acadc7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -13,104 +13,96 @@ # contents of this file. # -Aaron Bieber (qbit) +Jakob Borg (calmh) +Audrius Butkevicius (AudriusButkevicius) +Simon Frei (imsodin) +Tomasz Wilczyński <5626656+tomasz1986@users.noreply.github.com> +Alexander Graf (alex2108) +Alexandre Viau (aviau) +Anderson Mesquita (andersonvom) +André Colomb (acolomb) +Antony Male (canton7) +Ben Schulz (uok) +bt90 +Caleb Callaway (cqcallaw) +Daniel Harte (norgeous) +Emil Lundberg +Eric P +Evgeny Kuznetsov +greatroar <61184462+greatroar@users.noreply.github.com> +Lars K.W. Gohlke (lkwg82) +Lode Hoste (Zillode) +Michael Ploujnikov (plouj) +Ross Smith II (rasa) +Stefan Tatschner (rumpelsepp) +Wulf Weich (wweich) Adam Piggott (ProactiveServices) Adel Qalieh (adelq) -Alan Pope -Alberto Donato Aleksey Vasenev Alessandro G. (alessandro.g89) Alex Ionescu Alex Lindeman <139387+aelindeman@users.noreply.github.com> Alex Xu -Alexander Graf (alex2108) Alexander Seiler Alexandre Alves -Alexandre Viau (aviau) Aman Gupta -Anatoli Babenia -Anderson Mesquita (andersonvom) Andreas Sommer andresvia -Andrew Dunham (andrew-d) -Andrew Meyer Andrew Rabert (nvllsvm) <6550543+nvllsvm@users.noreply.github.com> Andrey D (scienmind) -André Colomb (acolomb) andyleap Anjan Momi Anthony Goeckner Antoine Lamielle (0x010C) -Antony Male (canton7) Anur Aranjedeath Arkadiusz Tymiński Aroun Arthur Axel fREW Schmidt (frioux) Artur Zubilewicz -Ashish Bhate -Audrius Butkevicius (AudriusButkevicius) Aurélien Rainone <476650+arl@users.noreply.github.com> BAHADIR YILMAZ Bart De Vries (mogwa1) Beat Reichenbach <44111292+beatreichenbach@users.noreply.github.com> -Ben Curthoys (bencurthoys) -Ben Schulz (uok) Ben Shepherd (benshep) Ben Sidhom (bsidhom) Benedikt Heine (bebehei) -Benedikt Morbach -Benjamin Nater <17193640+bn4t@users.noreply.github.com> Benno Fünfstück Benny Ng (tpng) boomsquared <54829195+boomsquared@users.noreply.github.com> Boqin Qin Boris Rybalkin -Brandon Philips (philips) Brendan Long (brendanlong) -Brian R. Becker (brbecker) -bt90 -Caleb Callaway (cqcallaw) -Carsten Hagemann (carstenhag) Catfriend1 <16361913+Catfriend1@users.noreply.github.com> Cathryne Linenweaver (Cathryne) Cedric Staniewski (xduugu) -chenrui Chih-Hsuan Yen <1937689+yan12125@users.noreply.github.com> Choongkyu Chris Howie (cdhowie) Chris Joel (cdata) -Chris Tonkinson Christian Kujau Christian Prescott chucic cjc7373 Colin Kennedy (moshen) Cromefire_ <26320625+cromefire@users.noreply.github.com> -cui fliter Cyprien Devillez d-volution <49024624+d-volution@users.noreply.github.com> -Dale Visser Dan Daniel Barczyk <46358936+DanielBarczyk@users.noreply.github.com> Daniel Bergmann (brgmnn) -Daniel Harte (norgeous) Daniel Martí (mvdan) Daniel Padrta <64928366+danpadcz@users.noreply.github.com> Darshil Chanpura (dtchanpura) dashangcun <907225865@qq.com> David Rimmer (dinosore) -deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> DeflateAwning <11021263+DeflateAwning@users.noreply.github.com> Denis A. (dva) Dennis Wilson (snnd) -dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> -dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> derekriemer DerRockWolf <50499906+DerRockWolf@users.noreply.github.com> desbma Devon G. Redekopp -diemade digital Dimitri Papadopoulos Orfanos <3234522+DimitriPapadopoulos@users.noreply.github.com> Dmitry Saveliev (dsaveliev) @@ -120,14 +112,11 @@ Dominik Heidler (asdil12) Elias Jarlebring (jarlebring) Elliot Huffman Emil Hessman (ceh) -Emil Lundberg Eng Zer Jun entity0xfe <109791748+entity0xfe@users.noreply.github.com> Eric Lesiuta -Eric P Erik Meitner (WSGCSysadmin) Evan Spensley <94762716+0evan@users.noreply.github.com> -Evgeny Kuznetsov Federico Castagnini (facastagnini) Felix <53702818+f-eliks@users.noreply.github.com> Felix Ableitner (Nutomic) @@ -141,7 +130,6 @@ ghjklw Gilli Sigurdsson (gillisig) Gleb Sinyavskiy Graham Miln (grahammiln) -greatroar <61184462+greatroar@users.noreply.github.com> Greg guangwu gudvinr @@ -156,50 +144,35 @@ Hugo Locurcio Iain Barnett Ian Johnson (anonymouse64) ignacy123 -Ikko Ashimine -Ilya Brin <464157+ilyabrin@users.noreply.github.com> Iskander Sharipov (Alex) Jaakko Hannikainen (jgke) -Jacek Szafarkiewicz (hadogenes) Jack Croft Jacob Jake Peterson (acogdev) -Jakob Borg (calmh) James O'Beirne James Patterson (jpjp) -janost Jaroslav Lichtblau Jaroslav Malec (dzarda) -jaseg Jaspitta -Jauder Ho Jaya Chithra (jayachithra) Jaya Kumar Jeffery To jelle van der Waa Jens Diemer (jedie) -Jerry Jacobs (xor-gate) -Jesse Lucas Jochen Voss (seehuhn) -Johan Andersson Johan Vromans (sciurius) John Rinehart (fuzzybear3965) Jonas Thelemann Jonathan -Jonathan Cross -Jonta <359397+Jonta@users.noreply.github.com> Jose Manuel Delicado (jmdaweb) jtagcat Julian Lehrhuber Jörg Thalheim Jędrzej Kula -K.B.Dharun Krishna -Kalle Laine Kapil Sareen Karol Różycki (krozycki) Kebin Liu Keith Harrison -Keith Turner Kelong Cong (kc1212) Ken'ichi Kamada (kamadak) Kevin Allen (ironmig) @@ -208,31 +181,23 @@ Kevin White, Jr. (kwhite17) klemens Kurt Fitzner (Kudalufi) kylosus <33132401+kylosus@users.noreply.github.com> -Lars K.W. Gohlke (lkwg82) Lars Lehtonen -Laurent Arnoud Laurent Etiemble (letiemble) Leo Arias (elopio) Liu Siyuan (liusy182) -Lode Hoste (Zillode) Lord Landon Agahnim (LordLandon) LSmithx2 <42276854+lsmithx2@users.noreply.github.com> -luchenhan <168071714+luchenhan@users.noreply.github.com> Lukas Lihotzki Luke Hamburg <1992842+luckman212@users.noreply.github.com> luzpaz Majed Abdulaziz (majedev) Marc Laporte (marclaporte) -Marc Pujol (kilburn) -Marcel Meyer Marcin Dziadus (marcindziadus) -marco-m Marcus B Spencer Marcus Legendre Mario Majila Mark Pulford (mpx) Martchus -Martin Polehla Mateusz Naściszewski (mateon1) Mateusz Ż mathias4833 <67101597+mathias4833@users.noreply.github.com> @@ -243,15 +208,10 @@ Matteo Ruina Maurizio Tomasi Max Max Schulze (kralo) -maxice8 <30738253+maxice8@users.noreply.github.com> MaximAL -Maxime Thirouin Maximilian -mclang <1721600+mclang@users.noreply.github.com> Michael Jephcote (Rewt0r) -Michael Ploujnikov (plouj) Michael Rienstra -Michael Tilli (pyfisch) MichaIng Migelo Mike Boone @@ -260,7 +220,6 @@ MikolajTwarog <43782609+MikolajTwarog@users.noreply.github.com> Mingxuan Lin mv1005 <49659413+mv1005@users.noreply.github.com> Nate Morrison (nrm21) -Naveen <172697+naveensrinivasan@users.noreply.github.com> nf Nicholas Rishel (PrototypeNM1) Nick Busey @@ -275,7 +234,6 @@ NoLooseEnds Oliver Freyermuth orangekame3 otbutz -Otiel overkill <22098433+0verk1ll@users.noreply.github.com> Oyebanji Jacob Mayowa Pablo @@ -283,7 +241,6 @@ Pascal Jungblut (pascalj) Paul Brit Paul Donald Pawel Palenica (qepasa) -Paweł Rozlach perewa Peter Badida Peter Dave Hello @@ -293,20 +250,15 @@ Phani Rithvij Phil Davis Philippe Schommers (filoozoom) Phill Luby (pluby) -Pier Paolo Ramon Piotr Bejda (piobpl) polyfloyd -Pramodh KP (pramodhkp) <1507241+pramodhkp@users.noreply.github.com> -pullmerge <166967364+pullmerge@users.noreply.github.com> Quentin Hibon Rahmi Pruitt red_led -Richard Hartmann Robert Carosi (nov1n) Roberto Santalla Robin Schoonover Roman Zaynetdinov (zaynetro) -Ross Smith II (rasa) rubenbe Ruslan Yevdokymov <38809160+ruslanye@users.noreply.github.com> Ryan Qian @@ -318,18 +270,14 @@ Sergey Mishin (ralder) Sertonix <83883937+Sertonix@users.noreply.github.com> Severin von Wnuck-Lipinski Shaarad Dalvi <60266155+shaaraddalvi@users.noreply.github.com> -Simon Frei (imsodin) Simon Mwepu Simon Pickup Sly_tom_cat Sonu Kumar Saw <31889738+dev-saw99@users.noreply.github.com> Stefan Kuntz (Stefan-Code) -Stefan Tatschner (rumpelsepp) Steven Eckhoff Suhas Gundimeda (snugghash) Sven Bachmann -Syncthing Automation -Syncthing Release Automation Sébastien WENSKE Taylor Khan (nelsonkhan) Terrance @@ -338,14 +286,11 @@ Thomas <9749173+uhthomas@users.noreply.github.com> Thomas Hipp Tim Abell (timabell) Tim Howes (timhowes) -Tim Nordenfur Tobias Frölich <40638719+tobifroe@users.noreply.github.com> Tobias Klauser Tobias Nygren (tnn2) Tobias Tom (tobiastom) Tom Jakubowski -Tomasz Wilczyński <5626656+tomasz1986@users.noreply.github.com> -Tommy Thorn Tommy van der Vorst Tully Robinson (tojrobinson) Tyler Brazier (tylerbrazier) @@ -363,10 +308,9 @@ WangXi Will Rouesnel William A. Kennington III (wkennington) wouter bolsterlee -Wulf Weich (wweich) xarx00 Xavier O. (damajor) -xjtdy888 (xjtdy888) +xjtdy888 (xjtdy888) Yannic A. (eipiminus1) 佛跳墙 落心 diff --git a/build.go b/build.go index c7f423d0d..b03fedf1c 100644 --- a/build.go +++ b/build.go @@ -38,27 +38,26 @@ import ( ) var ( - goarch string - goos string - noupgrade bool - version string - goCmd string - race bool - debug = os.Getenv("BUILDDEBUG") != "" - extraTags string - installSuffix string - pkgdir string - cc string - run string - benchRun string - buildOut string - debugBinary bool - coverage bool - long bool - timeout = "120s" - longTimeout = "600s" - numVersions = 5 - withNextGenGUI = os.Getenv("BUILD_NEXT_GEN_GUI") != "" + goarch string + goos string + noupgrade bool + version string + goCmd string + race bool + debug = os.Getenv("BUILDDEBUG") != "" + extraTags string + installSuffix string + pkgdir string + cc string + run string + benchRun string + buildOut string + debugBinary bool + coverage bool + long bool + timeout = "120s" + longTimeout = "600s" + numVersions = 5 ) type target struct { @@ -289,10 +288,10 @@ func runCommand(cmd string, target target) { build(target, tags) case "test": - test(strings.Fields(extraTags), "github.com/syncthing/syncthing/lib/...", "github.com/syncthing/syncthing/cmd/...") + test(strings.Fields(extraTags), "github.com/syncthing/syncthing/internal/...", "github.com/syncthing/syncthing/lib/...", "github.com/syncthing/syncthing/cmd/...") case "bench": - bench(strings.Fields(extraTags), "github.com/syncthing/syncthing/lib/...", "github.com/syncthing/syncthing/cmd/...") + bench(strings.Fields(extraTags), "github.com/syncthing/syncthing/internal/...", "github.com/syncthing/syncthing/lib/...", "github.com/syncthing/syncthing/cmd/...") case "integration": integration(false) @@ -380,7 +379,6 @@ func parseFlags() { flag.IntVar(&numVersions, "num-versions", numVersions, "Number of versions for changelog command") flag.StringVar(&run, "run", "", "Specify which tests to run") flag.StringVar(&benchRun, "bench", "", "Specify which benchmarks to run") - flag.BoolVar(&withNextGenGUI, "with-next-gen-gui", withNextGenGUI, "Also build 'newgui'") flag.StringVar(&buildOut, "build-out", "", "Set the '-o' value for 'go build'") flag.Parse() } @@ -453,10 +451,6 @@ func benchArgs() []string { } func install(target target, tags []string) { - if (target.name == "syncthing" || target.name == "") && !withNextGenGUI { - log.Println("Notice: Next generation GUI will not be built; see --with-next-gen-gui.") - } - lazyRebuildAssets() tags = append(target.tags, tags...) @@ -480,16 +474,12 @@ func install(target target, tags []string) { defer shouldCleanupSyso(sysoPath) } - args := []string{"install", "-v"} + args := []string{"install"} args = appendParameters(args, tags, target.buildPkgs...) runPrint(goCmd, args...) } func build(target target, tags []string) { - if (target.name == "syncthing" || target.name == "") && !withNextGenGUI { - log.Println("Notice: Next generation GUI will not be built; see --with-next-gen-gui.") - } - lazyRebuildAssets() tags = append(target.tags, tags...) @@ -512,7 +502,7 @@ func build(target target, tags []string) { defer shouldCleanupSyso(sysoPath) } - args := []string{"build", "-v"} + args := []string{"build"} if buildOut != "" { args = append(args, "-o", buildOut) } @@ -524,13 +514,6 @@ func setBuildEnvVars() { os.Setenv("GOOS", goos) os.Setenv("GOARCH", goarch) os.Setenv("CC", cc) - if os.Getenv("CGO_ENABLED") == "" { - switch goos { - case "darwin", "solaris": - default: - os.Setenv("CGO_ENABLED", "0") - } - } } func appendParameters(args []string, tags []string, pkgs ...string) []string { @@ -749,12 +732,9 @@ func shouldBuildSyso(dir string) (string, error) { sysoPath := filepath.Join(dir, "cmd", "syncthing", "resource.syso") // See https://github.com/josephspurrier/goversioninfo#command-line-flags - armOption := "" - if strings.Contains(goarch, "arm") { - armOption = "-arm=true" - } - - if _, err := runError("goversioninfo", "-o", sysoPath, armOption); err != nil { + arm := strings.HasPrefix(goarch, "arm") + a64 := strings.Contains(goarch, "64") + if _, err := runError("goversioninfo", "-o", sysoPath, fmt.Sprintf("-arm=%v", arm), fmt.Sprintf("-64=%v", a64)); err != nil { return "", errors.New("failed to create " + sysoPath + ": " + err.Error()) } @@ -826,43 +806,11 @@ func lazyRebuildAssets() { shouldRebuild := shouldRebuildAssets("lib/api/auto/gui.files.go", "gui") || shouldRebuildAssets("cmd/infra/strelaypoolsrv/auto/gui.files.go", "cmd/infra/strelaypoolsrv/gui") - if withNextGenGUI { - shouldRebuild = buildNextGenGUI() || shouldRebuild - } - if shouldRebuild { rebuildAssets() } } -func buildNextGenGUI() bool { - // Check if we need to run the npm process, and if so also set the flag - // to rebuild Go assets afterwards. The index.html is regenerated every - // time by the build process. This assumes the new GUI ends up in - // next-gen-gui/dist/next-gen-gui. - - if !shouldRebuildAssets("gui/next-gen-gui/index.html", "next-gen-gui") { - // The GUI is up to date. - return false - } - - runPrintInDir("next-gen-gui", "npm", "install") - runPrintInDir("next-gen-gui", "npm", "run", "build", "--", "--prod", "--subresource-integrity") - - rmr("gui/tech-ui") - - for _, src := range listFiles("next-gen-gui/dist") { - rel, _ := filepath.Rel("next-gen-gui/dist", src) - dst := filepath.Join("gui", rel) - if err := copyFile(src, dst, 0o644); err != nil { - fmt.Println("copy:", err) - os.Exit(1) - } - } - - return true -} - func shouldRebuildAssets(target, srcdir string) bool { info, err := os.Stat(target) if err != nil { @@ -974,6 +922,9 @@ func rmr(paths ...string) { } func getReleaseVersion() (string, error) { + if ver := os.Getenv("VERSION"); ver != "" { + return strings.TrimSpace(ver), nil + } bs, err := os.ReadFile("RELEASE") if err != nil { return "", err @@ -1018,7 +969,7 @@ func getGitVersion() (string, error) { } func getVersion() string { - // First try for a RELEASE file, + // First try for a RELEASE file or $VERSION env var, if ver, err := getReleaseVersion(); err == nil { return ver } diff --git a/cmd/dev/stfileinfo/main.go b/cmd/dev/stfileinfo/main.go index 805a1294b..4c1e2aedb 100644 --- a/cmd/dev/stfileinfo/main.go +++ b/cmd/dev/stfileinfo/main.go @@ -72,7 +72,7 @@ func main() { if *standardBlocks || blockSize < protocol.MinBlockSize { blockSize = protocol.BlockSize(fi.Size()) } - bs, err := scanner.Blocks(context.TODO(), fd, blockSize, fi.Size(), nil, true) + bs, err := scanner.Blocks(context.TODO(), fd, blockSize, fi.Size(), nil) if err != nil { log.Fatal(err) } diff --git a/cmd/syncthing/cli/debug.go b/cmd/syncthing/cli/debug.go index 9425e4310..9f18e1556 100644 --- a/cmd/syncthing/cli/debug.go +++ b/cmd/syncthing/cli/debug.go @@ -41,5 +41,4 @@ func (p *profileCommand) Run(ctx Context) error { type debugCommand struct { File fileCommand `cmd:"" help:"Show information about a file (or directory/symlink)"` Profile profileCommand `cmd:"" help:"Save a profile to help figuring out what Syncthing does"` - Index indexCommand `cmd:"" help:"Show information about the index (database)"` } diff --git a/cmd/syncthing/cli/index.go b/cmd/syncthing/cli/index.go deleted file mode 100644 index 04c9daa37..000000000 --- a/cmd/syncthing/cli/index.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (C) 2014 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package cli - -import ( - "github.com/alecthomas/kong" -) - -type indexCommand struct { - Dump struct{} `cmd:"" help:"Print the entire db"` - DumpSize struct{} `cmd:"" help:"Print the db size of different categories of information"` - Check struct{} `cmd:"" help:"Check the database for inconsistencies"` - Account struct{} `cmd:"" help:"Print key and value size statistics per key type"` -} - -func (*indexCommand) Run(kongCtx *kong.Context) error { - switch kongCtx.Selected().Name { - case "dump": - return indexDump() - case "dump-size": - return indexDumpSize() - case "check": - return indexCheck() - case "account": - return indexAccount() - } - return nil -} diff --git a/cmd/syncthing/cli/index_accounting.go b/cmd/syncthing/cli/index_accounting.go deleted file mode 100644 index cd22b16bb..000000000 --- a/cmd/syncthing/cli/index_accounting.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (C) 2020 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package cli - -import ( - "fmt" - "os" - "text/tabwriter" -) - -// indexAccount prints key and data size statistics per class -func indexAccount() error { - ldb, err := getDB() - if err != nil { - return err - } - - it, err := ldb.NewPrefixIterator(nil) - if err != nil { - return err - } - - var ksizes [256]int - var dsizes [256]int - var counts [256]int - var max [256]int - - for it.Next() { - key := it.Key() - t := key[0] - ds := len(it.Value()) - ks := len(key) - s := ks + ds - - counts[t]++ - ksizes[t] += ks - dsizes[t] += ds - if s > max[t] { - max[t] = s - } - } - - tw := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', tabwriter.AlignRight) - toti, totds, totks := 0, 0, 0 - for t := range ksizes { - if ksizes[t] > 0 { - // yes metric kilobytes 🤘 - fmt.Fprintf(tw, "0x%02x:\t%d items,\t%d KB keys +\t%d KB data,\t%d B +\t%d B avg,\t%d B max\t\n", t, counts[t], ksizes[t]/1000, dsizes[t]/1000, ksizes[t]/counts[t], dsizes[t]/counts[t], max[t]) - toti += counts[t] - totds += dsizes[t] - totks += ksizes[t] - } - } - fmt.Fprintf(tw, "Total\t%d items,\t%d KB keys +\t%d KB data.\t\n", toti, totks/1000, totds/1000) - tw.Flush() - - return nil -} diff --git a/cmd/syncthing/cli/index_dump.go b/cmd/syncthing/cli/index_dump.go deleted file mode 100644 index 6eff4717c..000000000 --- a/cmd/syncthing/cli/index_dump.go +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright (C) 2015 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package cli - -import ( - "encoding/binary" - "fmt" - "time" - - "google.golang.org/protobuf/proto" - - "github.com/syncthing/syncthing/internal/gen/bep" - "github.com/syncthing/syncthing/internal/gen/dbproto" - "github.com/syncthing/syncthing/lib/db" - "github.com/syncthing/syncthing/lib/protocol" -) - -func indexDump() error { - ldb, err := getDB() - if err != nil { - return err - } - it, err := ldb.NewPrefixIterator(nil) - if err != nil { - return err - } - for it.Next() { - key := it.Key() - switch key[0] { - case db.KeyTypeDevice: - folder := binary.BigEndian.Uint32(key[1:]) - device := binary.BigEndian.Uint32(key[1+4:]) - name := nulString(key[1+4+4:]) - fmt.Printf("[device] F:%d D:%d N:%q", folder, device, name) - - var f bep.FileInfo - err := proto.Unmarshal(it.Value(), &f) - if err != nil { - return err - } - fmt.Printf(" V:%v\n", &f) - - case db.KeyTypeGlobal: - folder := binary.BigEndian.Uint32(key[1:]) - name := nulString(key[1+4:]) - var flv dbproto.VersionList - proto.Unmarshal(it.Value(), &flv) - fmt.Printf("[global] F:%d N:%q V:%s\n", folder, name, &flv) - - case db.KeyTypeBlock: - folder := binary.BigEndian.Uint32(key[1:]) - hash := key[1+4 : 1+4+32] - name := nulString(key[1+4+32:]) - fmt.Printf("[block] F:%d H:%x N:%q I:%d\n", folder, hash, name, binary.BigEndian.Uint32(it.Value())) - - case db.KeyTypeDeviceStatistic: - fmt.Printf("[dstat] K:%x V:%x\n", key, it.Value()) - - case db.KeyTypeFolderStatistic: - fmt.Printf("[fstat] K:%x V:%x\n", key, it.Value()) - - case db.KeyTypeVirtualMtime: - folder := binary.BigEndian.Uint32(key[1:]) - name := nulString(key[1+4:]) - val := it.Value() - var realTime, virtualTime time.Time - realTime.UnmarshalBinary(val[:len(val)/2]) - virtualTime.UnmarshalBinary(val[len(val)/2:]) - fmt.Printf("[mtime] F:%d N:%q R:%v V:%v\n", folder, name, realTime, virtualTime) - - case db.KeyTypeFolderIdx: - key := binary.BigEndian.Uint32(key[1:]) - fmt.Printf("[folderidx] K:%d V:%q\n", key, it.Value()) - - case db.KeyTypeDeviceIdx: - key := binary.BigEndian.Uint32(key[1:]) - val := it.Value() - device := "" - if len(val) > 0 { - dev, err := protocol.DeviceIDFromBytes(val) - if err != nil { - device = fmt.Sprintf("", len(val)) - } else { - device = dev.String() - } - } - fmt.Printf("[deviceidx] K:%d V:%s\n", key, device) - - case db.KeyTypeIndexID: - device := binary.BigEndian.Uint32(key[1:]) - folder := binary.BigEndian.Uint32(key[5:]) - fmt.Printf("[indexid] D:%d F:%d I:%x\n", device, folder, it.Value()) - - case db.KeyTypeFolderMeta: - folder := binary.BigEndian.Uint32(key[1:]) - fmt.Printf("[foldermeta] F:%d", folder) - var cs dbproto.CountsSet - if err := proto.Unmarshal(it.Value(), &cs); err != nil { - fmt.Printf(" (invalid)\n") - } else { - fmt.Printf(" V:%v\n", &cs) - } - - case db.KeyTypeMiscData: - fmt.Printf("[miscdata] K:%q V:%q\n", key[1:], it.Value()) - - case db.KeyTypeSequence: - folder := binary.BigEndian.Uint32(key[1:]) - seq := binary.BigEndian.Uint64(key[5:]) - fmt.Printf("[sequence] F:%d S:%d V:%q\n", folder, seq, it.Value()) - - case db.KeyTypeNeed: - folder := binary.BigEndian.Uint32(key[1:]) - file := string(key[5:]) - fmt.Printf("[need] F:%d V:%q\n", folder, file) - - case db.KeyTypeBlockList: - fmt.Printf("[blocklist] H:%x\n", key[1:]) - - case db.KeyTypeBlockListMap: - folder := binary.BigEndian.Uint32(key[1:]) - hash := key[5:37] - fileName := string(key[37:]) - fmt.Printf("[blocklistmap] F:%d H:%x N:%s\n", folder, hash, fileName) - - case db.KeyTypeVersion: - fmt.Printf("[version] H:%x", key[1:]) - var v bep.Vector - err := proto.Unmarshal(it.Value(), &v) - if err != nil { - fmt.Printf(" (invalid)\n") - } else { - fmt.Printf(" V:%v\n", &v) - } - - case db.KeyTypePendingFolder: - device := binary.BigEndian.Uint32(key[1:]) - folder := string(key[5:]) - var of dbproto.ObservedFolder - proto.Unmarshal(it.Value(), &of) - fmt.Printf("[pendingFolder] D:%d F:%s V:%v\n", device, folder, &of) - - case db.KeyTypePendingDevice: - device := "" - dev, err := protocol.DeviceIDFromBytes(key[1:]) - if err == nil { - device = dev.String() - } - var od dbproto.ObservedDevice - proto.Unmarshal(it.Value(), &od) - fmt.Printf("[pendingDevice] D:%v V:%v\n", device, &od) - - default: - fmt.Printf("[??? %d]\n %x\n %x\n", key[0], key, it.Value()) - } - } - return nil -} diff --git a/cmd/syncthing/cli/index_dumpsize.go b/cmd/syncthing/cli/index_dumpsize.go deleted file mode 100644 index 17cfa61c4..000000000 --- a/cmd/syncthing/cli/index_dumpsize.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (C) 2015 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package cli - -import ( - "cmp" - "encoding/binary" - "fmt" - "slices" - - "github.com/syncthing/syncthing/lib/db" -) - -func indexDumpSize() error { - type sizedElement struct { - key string - size int - } - - ldb, err := getDB() - if err != nil { - return err - } - - it, err := ldb.NewPrefixIterator(nil) - if err != nil { - return err - } - - var elems []sizedElement - for it.Next() { - var ele sizedElement - - key := it.Key() - switch key[0] { - case db.KeyTypeDevice: - folder := binary.BigEndian.Uint32(key[1:]) - device := binary.BigEndian.Uint32(key[1+4:]) - name := nulString(key[1+4+4:]) - ele.key = fmt.Sprintf("DEVICE:%d:%d:%s", folder, device, name) - - case db.KeyTypeGlobal: - folder := binary.BigEndian.Uint32(key[1:]) - name := nulString(key[1+4:]) - ele.key = fmt.Sprintf("GLOBAL:%d:%s", folder, name) - - case db.KeyTypeBlock: - folder := binary.BigEndian.Uint32(key[1:]) - hash := key[1+4 : 1+4+32] - name := nulString(key[1+4+32:]) - ele.key = fmt.Sprintf("BLOCK:%d:%x:%s", folder, hash, name) - - case db.KeyTypeDeviceStatistic: - ele.key = fmt.Sprintf("DEVICESTATS:%s", key[1:]) - - case db.KeyTypeFolderStatistic: - ele.key = fmt.Sprintf("FOLDERSTATS:%s", key[1:]) - - case db.KeyTypeVirtualMtime: - ele.key = fmt.Sprintf("MTIME:%s", key[1:]) - - case db.KeyTypeFolderIdx: - id := binary.BigEndian.Uint32(key[1:]) - ele.key = fmt.Sprintf("FOLDERIDX:%d", id) - - case db.KeyTypeDeviceIdx: - id := binary.BigEndian.Uint32(key[1:]) - ele.key = fmt.Sprintf("DEVICEIDX:%d", id) - - default: - ele.key = fmt.Sprintf("UNKNOWN:%x", key) - } - ele.size = len(it.Value()) - elems = append(elems, ele) - } - - slices.SortFunc(elems, func(a, b sizedElement) int { - return cmp.Compare(b.size, a.size) - }) - for _, ele := range elems { - fmt.Println(ele.key, ele.size) - } - - return nil -} diff --git a/cmd/syncthing/cli/index_idxck.go b/cmd/syncthing/cli/index_idxck.go deleted file mode 100644 index 4e1cbc181..000000000 --- a/cmd/syncthing/cli/index_idxck.go +++ /dev/null @@ -1,435 +0,0 @@ -// Copyright (C) 2018 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package cli - -import ( - "bytes" - "cmp" - "encoding/binary" - "errors" - "fmt" - "slices" - - "google.golang.org/protobuf/proto" - - "github.com/syncthing/syncthing/internal/gen/bep" - "github.com/syncthing/syncthing/internal/gen/dbproto" - "github.com/syncthing/syncthing/lib/db" - "github.com/syncthing/syncthing/lib/protocol" -) - -type fileInfoKey struct { - folder uint32 - device uint32 - name string -} - -type globalKey struct { - folder uint32 - name string -} - -type sequenceKey struct { - folder uint32 - sequence uint64 -} - -func indexCheck() (err error) { - ldb, err := getDB() - if err != nil { - return err - } - - folders := make(map[uint32]string) - devices := make(map[uint32]string) - deviceToIDs := make(map[string]uint32) - fileInfos := make(map[fileInfoKey]*bep.FileInfo) - globals := make(map[globalKey]*dbproto.VersionList) - sequences := make(map[sequenceKey]string) - needs := make(map[globalKey]struct{}) - blocklists := make(map[string]struct{}) - versions := make(map[string]*bep.Vector) - usedBlocklists := make(map[string]struct{}) - usedVersions := make(map[string]struct{}) - var localDeviceKey uint32 - success := true - defer func() { - if err == nil { - if success { - fmt.Println("Index check completed successfully.") - } else { - err = errors.New("Inconsistencies found in the index") - } - } - }() - - it, err := ldb.NewPrefixIterator(nil) - if err != nil { - return err - } - for it.Next() { - key := it.Key() - switch key[0] { - case db.KeyTypeDevice: - folder := binary.BigEndian.Uint32(key[1:]) - device := binary.BigEndian.Uint32(key[1+4:]) - name := nulString(key[1+4+4:]) - - var f bep.FileInfo - err := proto.Unmarshal(it.Value(), &f) - if err != nil { - fmt.Println("Unable to unmarshal FileInfo:", err) - success = false - continue - } - - fileInfos[fileInfoKey{folder, device, name}] = &f - - case db.KeyTypeGlobal: - folder := binary.BigEndian.Uint32(key[1:]) - name := nulString(key[1+4:]) - var flv dbproto.VersionList - if err := proto.Unmarshal(it.Value(), &flv); err != nil { - fmt.Println("Unable to unmarshal VersionList:", err) - success = false - continue - } - globals[globalKey{folder, name}] = &flv - - case db.KeyTypeFolderIdx: - key := binary.BigEndian.Uint32(it.Key()[1:]) - folders[key] = string(it.Value()) - - case db.KeyTypeDeviceIdx: - key := binary.BigEndian.Uint32(it.Key()[1:]) - devices[key] = string(it.Value()) - deviceToIDs[string(it.Value())] = key - if bytes.Equal(it.Value(), protocol.LocalDeviceID[:]) { - localDeviceKey = key - } - - case db.KeyTypeSequence: - folder := binary.BigEndian.Uint32(key[1:]) - seq := binary.BigEndian.Uint64(key[5:]) - val := it.Value() - sequences[sequenceKey{folder, seq}] = string(val[9:]) - - case db.KeyTypeNeed: - folder := binary.BigEndian.Uint32(key[1:]) - name := nulString(key[1+4:]) - needs[globalKey{folder, name}] = struct{}{} - - case db.KeyTypeBlockList: - hash := string(key[1:]) - blocklists[hash] = struct{}{} - - case db.KeyTypeVersion: - hash := string(key[1:]) - var v bep.Vector - if err := proto.Unmarshal(it.Value(), &v); err != nil { - fmt.Println("Unable to unmarshal Vector:", err) - success = false - continue - } - versions[hash] = &v - } - } - - if localDeviceKey == 0 { - fmt.Println("Missing key for local device in device index (bailing out)") - success = false - return - } - - var missingSeq []sequenceKey - for fk, fi := range fileInfos { - if fk.name != fi.Name { - fmt.Printf("Mismatching FileInfo name, %q (key) != %q (actual)\n", fk.name, fi.Name) - success = false - } - - folder := folders[fk.folder] - if folder == "" { - fmt.Printf("Unknown folder ID %d for FileInfo %q\n", fk.folder, fk.name) - success = false - continue - } - if devices[fk.device] == "" { - fmt.Printf("Unknown device ID %d for FileInfo %q, folder %q\n", fk.folder, fk.name, folder) - success = false - } - - if fk.device == localDeviceKey { - sk := sequenceKey{fk.folder, uint64(fi.Sequence)} - name, ok := sequences[sk] - if !ok { - fmt.Printf("Sequence entry missing for FileInfo %q, folder %q, seq %d\n", fi.Name, folder, fi.Sequence) - missingSeq = append(missingSeq, sk) - success = false - continue - } - if name != fi.Name { - fmt.Printf("Sequence entry refers to wrong name, %q (seq) != %q (FileInfo), folder %q, seq %d\n", name, fi.Name, folder, fi.Sequence) - success = false - } - } - - if len(fi.Blocks) == 0 && len(fi.BlocksHash) != 0 { - key := string(fi.BlocksHash) - if _, ok := blocklists[key]; !ok { - fmt.Printf("Missing block list for file %q, block list hash %x\n", fi.Name, fi.BlocksHash) - success = false - } else { - usedBlocklists[key] = struct{}{} - } - } - - if fi.VersionHash != nil { - key := string(fi.VersionHash) - if _, ok := versions[key]; !ok { - fmt.Printf("Missing version vector for file %q, version hash %x\n", fi.Name, fi.VersionHash) - success = false - } else { - usedVersions[key] = struct{}{} - } - } - - _, ok := globals[globalKey{fk.folder, fk.name}] - if !ok { - fmt.Printf("Missing global for file %q\n", fi.Name) - success = false - continue - } - } - - // Aggregate the ranges of missing sequence entries, print them - - slices.SortFunc(missingSeq, func(a, b sequenceKey) int { - if a.folder != b.folder { - return cmp.Compare(a.folder, b.folder) - } - return cmp.Compare(a.sequence, b.sequence) - }) - - var folder uint32 - var startSeq, prevSeq uint64 - for _, sk := range missingSeq { - if folder != sk.folder || sk.sequence != prevSeq+1 { - if folder != 0 { - fmt.Printf("Folder %d missing %d sequence entries: #%d - #%d\n", folder, prevSeq-startSeq+1, startSeq, prevSeq) - } - startSeq = sk.sequence - folder = sk.folder - } - prevSeq = sk.sequence - } - if folder != 0 { - fmt.Printf("Folder %d missing %d sequence entries: #%d - #%d\n", folder, prevSeq-startSeq+1, startSeq, prevSeq) - } - - for gk, vl := range globals { - folder := folders[gk.folder] - if folder == "" { - fmt.Printf("Unknown folder ID %d for VersionList %q\n", gk.folder, gk.name) - success = false - } - checkGlobal := func(i int, device []byte, version protocol.Vector, invalid, deleted bool) { - dev, ok := deviceToIDs[string(device)] - if !ok { - fmt.Printf("VersionList %q, folder %q refers to unknown device %q\n", gk.name, folder, device) - success = false - } - fi, ok := fileInfos[fileInfoKey{gk.folder, dev, gk.name}] - if !ok { - fmt.Printf("VersionList %q, folder %q, entry %d refers to unknown FileInfo\n", gk.name, folder, i) - success = false - } - - fiv := fi.Version - if fi.VersionHash != nil { - fiv = versions[string(fi.VersionHash)] - } - if !protocol.VectorFromWire(fiv).Equal(version) { - fmt.Printf("VersionList %q, folder %q, entry %d, FileInfo version mismatch, %v (VersionList) != %v (FileInfo)\n", gk.name, folder, i, version, fi.Version) - success = false - } - ffi := protocol.FileInfoFromDB(fi) - if ffi.IsInvalid() != invalid { - fmt.Printf("VersionList %q, folder %q, entry %d, FileInfo invalid mismatch, %v (VersionList) != %v (FileInfo)\n", gk.name, folder, i, invalid, ffi.IsInvalid()) - success = false - } - if ffi.IsDeleted() != deleted { - fmt.Printf("VersionList %q, folder %q, entry %d, FileInfo deleted mismatch, %v (VersionList) != %v (FileInfo)\n", gk.name, folder, i, deleted, ffi.IsDeleted()) - success = false - } - } - for i, fv := range vl.Versions { - ver := protocol.VectorFromWire(fv.Version) - for _, device := range fv.Devices { - checkGlobal(i, device, ver, false, fv.Deleted) - } - for _, device := range fv.InvalidDevices { - checkGlobal(i, device, ver, true, fv.Deleted) - } - } - - // If we need this file we should have a need entry for it. False - // positives from needsLocally for deleted files, where we might - // legitimately lack an entry if we never had it, and ignored files. - if needsLocally(vl) { - _, ok := needs[gk] - if !ok { - fv, _ := vlGetGlobal(vl) - devB, _ := fvFirstDevice(fv) - dev := deviceToIDs[string(devB)] - fi := protocol.FileInfoFromDB(fileInfos[fileInfoKey{gk.folder, dev, gk.name}]) - if !fi.IsDeleted() && !fi.IsIgnored() { - fmt.Printf("Missing need entry for needed file %q, folder %q\n", gk.name, folder) - } - } - } - } - - seenSeq := make(map[fileInfoKey]uint64) - for sk, name := range sequences { - folder := folders[sk.folder] - if folder == "" { - fmt.Printf("Unknown folder ID %d for sequence entry %d, %q\n", sk.folder, sk.sequence, name) - success = false - continue - } - - if prev, ok := seenSeq[fileInfoKey{folder: sk.folder, name: name}]; ok { - fmt.Printf("Duplicate sequence entry for %q, folder %q, seq %d (prev %d)\n", name, folder, sk.sequence, prev) - success = false - } - seenSeq[fileInfoKey{folder: sk.folder, name: name}] = sk.sequence - - fi, ok := fileInfos[fileInfoKey{sk.folder, localDeviceKey, name}] - if !ok { - fmt.Printf("Missing FileInfo for sequence entry %d, folder %q, %q\n", sk.sequence, folder, name) - success = false - continue - } - if fi.Sequence != int64(sk.sequence) { - fmt.Printf("Sequence mismatch for %q, folder %q, %d (key) != %d (FileInfo)\n", name, folder, sk.sequence, fi.Sequence) - success = false - } - } - - for nk := range needs { - folder := folders[nk.folder] - if folder == "" { - fmt.Printf("Unknown folder ID %d for need entry %q\n", nk.folder, nk.name) - success = false - continue - } - - vl, ok := globals[nk] - if !ok { - fmt.Printf("Missing global for need entry %q, folder %q\n", nk.name, folder) - success = false - continue - } - - if !needsLocally(vl) { - fmt.Printf("Need entry for file we don't need, %q, folder %q\n", nk.name, folder) - success = false - } - } - - if d := len(blocklists) - len(usedBlocklists); d > 0 { - fmt.Printf("%d block list entries out of %d needs GC\n", d, len(blocklists)) - } - if d := len(versions) - len(usedVersions); d > 0 { - fmt.Printf("%d version entries out of %d needs GC\n", d, len(versions)) - } - - return nil -} - -func needsLocally(vl *dbproto.VersionList) bool { - gfv, gok := vlGetGlobal(vl) - if !gok { // That's weird, but we hardly need something non-existent - return false - } - fv, ok := vlGet(vl, protocol.LocalDeviceID[:]) - return db.Need(gfv, ok, protocol.VectorFromWire(fv.Version)) -} - -// Get returns a FileVersion that contains the given device and whether it has -// been found at all. -func vlGet(vl *dbproto.VersionList, device []byte) (*dbproto.FileVersion, bool) { - _, i, _, ok := vlFindDevice(vl, device) - if !ok { - return &dbproto.FileVersion{}, false - } - return vl.Versions[i], true -} - -// GetGlobal returns the current global FileVersion. The returned FileVersion -// may be invalid, if all FileVersions are invalid. Returns false only if -// VersionList is empty. -func vlGetGlobal(vl *dbproto.VersionList) (*dbproto.FileVersion, bool) { - i := vlFindGlobal(vl) - if i == -1 { - return nil, false - } - return vl.Versions[i], true -} - -// findGlobal returns the first version that isn't invalid, or if all versions are -// invalid just the first version (i.e. 0) or -1, if there's no versions at all. -func vlFindGlobal(vl *dbproto.VersionList) int { - for i := range vl.Versions { - if !fvIsInvalid(vl.Versions[i]) { - return i - } - } - if len(vl.Versions) == 0 { - return -1 - } - return 0 -} - -// findDevice returns whether the device is in InvalidVersions or Versions and -// in InvalidDevices or Devices (true for invalid), the positions in the version -// and device slices and whether it has been found at all. -func vlFindDevice(vl *dbproto.VersionList, device []byte) (bool, int, int, bool) { - for i, v := range vl.Versions { - if j := deviceIndex(v.Devices, device); j != -1 { - return false, i, j, true - } - if j := deviceIndex(v.InvalidDevices, device); j != -1 { - return true, i, j, true - } - } - return false, -1, -1, false -} - -func deviceIndex(devices [][]byte, device []byte) int { - for i, dev := range devices { - if bytes.Equal(device, dev) { - return i - } - } - return -1 -} - -func fvFirstDevice(fv *dbproto.FileVersion) ([]byte, bool) { - if len(fv.Devices) != 0 { - return fv.Devices[0], true - } - if len(fv.InvalidDevices) != 0 { - return fv.InvalidDevices[0], true - } - return nil, false -} - -func fvIsInvalid(fv *dbproto.FileVersion) bool { - return fv == nil || len(fv.Devices) == 0 -} diff --git a/cmd/syncthing/cli/main.go b/cmd/syncthing/cli/main.go index 82367cc67..7422b6eb2 100644 --- a/cmd/syncthing/cli/main.go +++ b/cmd/syncthing/cli/main.go @@ -14,15 +14,12 @@ import ( "github.com/alecthomas/kong" "github.com/kballard/go-shellquote" - "github.com/syncthing/syncthing/cmd/syncthing/cmdutil" "github.com/syncthing/syncthing/lib/config" ) type CLI struct { - cmdutil.CommonOptions - DataDir string `name:"data" placeholder:"PATH" env:"STDATADIR" help:"Set data directory (database and logs)"` - GUIAddress string `name:"gui-address"` - GUIAPIKey string `name:"gui-apikey"` + GUIAddress string `name:"gui-address" env:"STGUIADDRESS"` + GUIAPIKey string `name:"gui-apikey" env:"STGUIAPIKEY"` Show showCommand `cmd:"" help:"Show command group"` Debug debugCommand `cmd:"" help:"Debug command group"` @@ -37,11 +34,6 @@ type Context struct { } func (cli CLI) AfterApply(kongCtx *kong.Context) error { - err := cmdutil.SetConfigDataLocationsFromFlags(cli.HomeDir, cli.ConfDir, cli.DataDir) - if err != nil { - return fmt.Errorf("command line options: %w", err) - } - clientFactory := &apiClientFactory{ cfg: config.GUIConfiguration{ RawAddress: cli.GUIAddress, diff --git a/cmd/syncthing/cli/utils.go b/cmd/syncthing/cli/utils.go index 7b69bbf57..fde611575 100644 --- a/cmd/syncthing/cli/utils.go +++ b/cmd/syncthing/cli/utils.go @@ -17,8 +17,6 @@ import ( "path/filepath" "github.com/syncthing/syncthing/lib/config" - "github.com/syncthing/syncthing/lib/db/backend" - "github.com/syncthing/syncthing/lib/locations" ) func responseToBArray(response *http.Response) ([]byte, error) { @@ -133,10 +131,6 @@ func prettyPrintResponse(response *http.Response) error { return prettyPrintJSON(data) } -func getDB() (backend.Backend, error) { - return backend.OpenLevelDBRO(locations.Get(locations.Database)) -} - func nulString(bs []byte) string { for i := range bs { if bs[i] == 0 { diff --git a/cmd/syncthing/cmdutil/options_common.go b/cmd/syncthing/cmdutil/options_common.go deleted file mode 100644 index 1a7a30999..000000000 --- a/cmd/syncthing/cmdutil/options_common.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (C) 2021 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package cmdutil - -// CommonOptions are reused among several subcommands -type CommonOptions struct { - buildCommonOptions - ConfDir string `name:"config" placeholder:"PATH" env:"STCONFDIR" help:"Set configuration directory (config and keys)"` - HomeDir string `name:"home" placeholder:"PATH" env:"STHOMEDIR" help:"Set configuration and data directory"` - NoDefaultFolder bool `env:"STNODEFAULTFOLDER" help:"Don't create the \"default\" folder on first startup"` - SkipPortProbing bool `help:"Don't try to find free ports for GUI and listen addresses on first startup"` -} diff --git a/cmd/syncthing/cmdutil/util.go b/cmd/syncthing/cmdutil/util.go deleted file mode 100644 index 6a44456b0..000000000 --- a/cmd/syncthing/cmdutil/util.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (C) 2014 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package cmdutil - -import ( - "errors" - - "github.com/syncthing/syncthing/lib/locations" -) - -func SetConfigDataLocationsFromFlags(homeDir, confDir, dataDir string) error { - homeSet := homeDir != "" - confSet := confDir != "" - dataSet := dataDir != "" - switch { - case dataSet != confSet: - return errors.New("either both or none of --config and --data must be given, use --home to set both at once") - case homeSet && dataSet: - return errors.New("--home must not be used together with --config and --data") - case homeSet: - confDir = homeDir - dataDir = homeDir - fallthrough - case dataSet: - if err := locations.SetBaseDir(locations.ConfigBaseDir, confDir); err != nil { - return err - } - return locations.SetBaseDir(locations.DataBaseDir, dataDir) - } - return nil -} diff --git a/cmd/syncthing/decrypt/decrypt.go b/cmd/syncthing/decrypt/decrypt.go index ed162964c..4545e119c 100644 --- a/cmd/syncthing/decrypt/decrypt.go +++ b/cmd/syncthing/decrypt/decrypt.go @@ -238,7 +238,7 @@ func (c *CLI) decryptFile(encFi *protocol.FileInfo, plainFi *protocol.FileInfo, } // Verify the hash against the plaintext block info - if !scanner.Validate(dec, plainBlock.Hash, 0) { + if !scanner.Validate(dec, plainBlock.Hash) { // The block decrypted correctly but fails the hash check. This // is odd and unexpected, but it it's still a valid block from // the source. The file might have changed while we pulled it? diff --git a/cmd/syncthing/generate/generate.go b/cmd/syncthing/generate/generate.go index 5446cb3ba..f9d9814f0 100644 --- a/cmd/syncthing/generate/generate.go +++ b/cmd/syncthing/generate/generate.go @@ -11,42 +11,25 @@ import ( "bufio" "context" "crypto/tls" - "errors" "fmt" "os" - "github.com/syncthing/syncthing/cmd/syncthing/cmdutil" "github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/events" "github.com/syncthing/syncthing/lib/fs" "github.com/syncthing/syncthing/lib/locations" "github.com/syncthing/syncthing/lib/logger" - "github.com/syncthing/syncthing/lib/osutil" "github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/syncthing" ) type CLI struct { - cmdutil.CommonOptions - GUIUser string `placeholder:"STRING" help:"Specify new GUI authentication user name"` - GUIPassword string `placeholder:"STRING" help:"Specify new GUI authentication password (use - to read from standard input)"` + GUIUser string `placeholder:"STRING" help:"Specify new GUI authentication user name"` + GUIPassword string `placeholder:"STRING" help:"Specify new GUI authentication password (use - to read from standard input)"` + NoPortProbing bool `help:"Don't try to find free ports for GUI and listen addresses on first startup" env:"STNOPORTPROBING"` } func (c *CLI) Run(l logger.Logger) error { - if c.HideConsole { - osutil.HideConsole() - } - - if c.HomeDir != "" { - if c.ConfDir != "" { - return errors.New("--home must not be used together with --config") - } - c.ConfDir = c.HomeDir - } - if c.ConfDir == "" { - c.ConfDir = locations.GetBaseDir(locations.ConfigBaseDir) - } - // Support reading the password from a pipe or similar if c.GUIPassword == "-" { reader := bufio.NewReader(os.Stdin) @@ -57,13 +40,13 @@ func (c *CLI) Run(l logger.Logger) error { c.GUIPassword = string(password) } - if err := Generate(l, c.ConfDir, c.GUIUser, c.GUIPassword, c.NoDefaultFolder, c.SkipPortProbing); err != nil { + if err := Generate(l, locations.GetBaseDir(locations.ConfigBaseDir), c.GUIUser, c.GUIPassword, c.NoPortProbing); err != nil { return fmt.Errorf("failed to generate config and keys: %w", err) } return nil } -func Generate(l logger.Logger, confDir, guiUser, guiPassword string, noDefaultFolder, skipPortProbing bool) error { +func Generate(l logger.Logger, confDir, guiUser, guiPassword string, skipPortProbing bool) error { dir, err := fs.ExpandTilde(confDir) if err != nil { return err @@ -91,7 +74,7 @@ func Generate(l logger.Logger, confDir, guiUser, guiPassword string, noDefaultFo cfgFile := locations.Get(locations.ConfigFile) cfg, _, err := config.Load(cfgFile, myID, events.NoopLogger) if fs.IsNotExist(err) { - if cfg, err = syncthing.DefaultConfig(cfgFile, myID, events.NoopLogger, noDefaultFolder, skipPortProbing); err != nil { + if cfg, err = syncthing.DefaultConfig(cfgFile, myID, events.NoopLogger, skipPortProbing); err != nil { return fmt.Errorf("create config: %w", err) } } else if err != nil { diff --git a/cmd/syncthing/cmdutil/options_others.go b/cmd/syncthing/hideconsole_others.go similarity index 86% rename from cmd/syncthing/cmdutil/options_others.go rename to cmd/syncthing/hideconsole_others.go index e299ba48c..0688dac1c 100644 --- a/cmd/syncthing/cmdutil/options_others.go +++ b/cmd/syncthing/hideconsole_others.go @@ -7,8 +7,8 @@ //go:build !windows // +build !windows -package cmdutil +package main -type buildCommonOptions struct { +type buildSpecificOptions struct { HideConsole bool `hidden:""` } diff --git a/cmd/syncthing/cmdutil/options_windows.go b/cmd/syncthing/hideconsole_windows.go similarity index 65% rename from cmd/syncthing/cmdutil/options_windows.go rename to cmd/syncthing/hideconsole_windows.go index 99fb50d5b..f51e8acc3 100644 --- a/cmd/syncthing/cmdutil/options_windows.go +++ b/cmd/syncthing/hideconsole_windows.go @@ -4,8 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at https://mozilla.org/MPL/2.0/. -package cmdutil +package main -type buildCommonOptions struct { - HideConsole bool `name:"no-console" help:"Hide console window"` +type buildSpecificOptions struct { + HideConsole bool `name:"no-console" help:"Hide console window" env:"STHIDECONSOLE"` } diff --git a/cmd/syncthing/main.go b/cmd/syncthing/main.go index 01256fec1..47cf7702f 100644 --- a/cmd/syncthing/main.go +++ b/cmd/syncthing/main.go @@ -8,6 +8,7 @@ package main import ( "bytes" + "cmp" "context" "crypto/tls" "errors" @@ -25,8 +26,8 @@ import ( "runtime/pprof" "slices" "strconv" - "strings" "syscall" + "text/tabwriter" "time" "github.com/alecthomas/kong" @@ -35,13 +36,13 @@ import ( "github.com/willabides/kongplete" "github.com/syncthing/syncthing/cmd/syncthing/cli" - "github.com/syncthing/syncthing/cmd/syncthing/cmdutil" "github.com/syncthing/syncthing/cmd/syncthing/decrypt" "github.com/syncthing/syncthing/cmd/syncthing/generate" + "github.com/syncthing/syncthing/internal/db" + "github.com/syncthing/syncthing/internal/db/sqlite" _ "github.com/syncthing/syncthing/lib/automaxprocs" "github.com/syncthing/syncthing/lib/build" "github.com/syncthing/syncthing/lib/config" - "github.com/syncthing/syncthing/lib/db" "github.com/syncthing/syncthing/lib/dialer" "github.com/syncthing/syncthing/lib/events" "github.com/syncthing/syncthing/lib/fs" @@ -128,53 +129,67 @@ var ( // The entrypoint struct is the main entry point for the command line parser. The // commands and options here are top level commands to syncthing. // Cli is just a placeholder for the help text (see main). -var entrypoint struct { - Serve serveOptions `cmd:"" help:"Run Syncthing"` - Generate generate.CLI `cmd:"" help:"Generate key and config, then exit"` - Decrypt decrypt.CLI `cmd:"" help:"Decrypt or verify an encrypted folder"` - Cli cli.CLI `cmd:"" help:"Command line interface for Syncthing"` +type CLI struct { + // The directory options are defined at top level and available for all + // subcommands. Their settings take effect on the `locations` package by + // way of the command line parser, so anything using `locations.Get` etc + // will be doing the right thing. + ConfDir string `name:"config" short:"C" placeholder:"PATH" env:"STCONFDIR" help:"Set configuration directory (config and keys)"` + DataDir string `name:"data" short:"D" placeholder:"PATH" env:"STDATADIR" help:"Set data directory (database and logs)"` + HomeDir string `name:"home" short:"H" placeholder:"PATH" env:"STHOMEDIR" help:"Set configuration and data directory"` + + Serve serveCmd `cmd:"" help:"Run Syncthing (default)" default:"withargs"` + CLI cli.CLI `cmd:"" help:"Command line interface for Syncthing"` + + Browser browserCmd `cmd:"" help:"Open GUI in browser, then exit"` + Decrypt decrypt.CLI `cmd:"" help:"Decrypt or verify an encrypted folder"` + DeviceID deviceIDCmd `cmd:"" help:"Show device ID, then exit"` + Generate generate.CLI `cmd:"" help:"Generate key and config, then exit"` + Paths pathsCmd `cmd:"" help:"Show configuration paths, then exit"` + Upgrade upgradeCmd `cmd:"" help:"Perform or check for upgrade, then exit"` + Version versionCmd `cmd:"" help:"Show current version, then exit"` + Debug debugCmd `cmd:"" help:"Various debugging commands"` + InstallCompletions kongplete.InstallCompletions `cmd:"" help:"Print commands to install shell completions"` } -// serveOptions are the options for the `syncthing serve` command. -type serveOptions struct { - cmdutil.CommonOptions - AllowNewerConfig bool `help:"Allow loading newer than current config version"` - Audit bool `help:"Write events to audit file"` - AuditFile string `name:"auditfile" placeholder:"PATH" help:"Specify audit file (use \"-\" for stdout, \"--\" for stderr)"` - BrowserOnly bool `help:"Open GUI in browser"` - DataDir string `name:"data" placeholder:"PATH" env:"STDATADIR" help:"Set data directory (database and logs)"` - DeviceID bool `help:"Show the device ID"` - GenerateDir string `name:"generate" placeholder:"PATH" help:"Generate key and config in specified dir, then exit"` // DEPRECATED: replaced by subcommand! - GUIAddress string `name:"gui-address" placeholder:"URL" help:"Override GUI address (e.g. \"http://192.0.2.42:8443\")"` - GUIAPIKey string `name:"gui-apikey" placeholder:"API-KEY" help:"Override GUI API key"` - LogFile string `name:"logfile" default:"${logFile}" placeholder:"PATH" help:"Log file name (see below)"` - LogFlags int `name:"logflags" default:"${logFlags}" placeholder:"BITS" help:"Select information in log line prefix (see below)"` - LogMaxFiles int `placeholder:"N" default:"${logMaxFiles}" name:"log-max-old-files" help:"Number of old files to keep (zero to keep only current)"` - LogMaxSize int `placeholder:"BYTES" default:"${logMaxSize}" help:"Maximum size of any file (zero to disable log rotation)"` - NoBrowser bool `help:"Do not start browser"` - NoRestart bool `env:"STNORESTART" help:"Do not restart Syncthing when exiting due to API/GUI command, upgrade, or crash"` - NoUpgrade bool `env:"STNOUPGRADE" help:"Disable automatic upgrades"` - Paths bool `help:"Show configuration paths"` - Paused bool `help:"Start with all devices and folders paused"` - Unpaused bool `help:"Start with all devices and folders unpaused"` - Upgrade bool `help:"Perform upgrade"` - UpgradeCheck bool `help:"Check for available upgrade"` - UpgradeTo string `placeholder:"URL" help:"Force upgrade directly from specified URL"` - Verbose bool `help:"Print verbose log output"` - Version bool `help:"Show version"` +func (c *CLI) AfterApply() error { + // Executed after parsing command line options but before running actual + // subcommands + return setConfigDataLocationsFromFlags(c.HomeDir, c.ConfDir, c.DataDir) +} + +// serveCmd are the options for the `syncthing serve` command. +type serveCmd struct { + buildSpecificOptions + + AllowNewerConfig bool `help:"Allow loading newer than current config version" env:"STALLOWNEWERCONFIG"` + Audit bool `help:"Write events to audit file" env:"STAUDIT"` + AuditFile string `name:"auditfile" help:"Specify audit file (use \"-\" for stdout, \"--\" for stderr)" placeholder:"PATH" env:"STAUDITFILE"` + DBMaintenanceInterval time.Duration `help:"Database maintenance interval" default:"8h" env:"STDBMAINTENANCEINTERVAL"` + DBDeleteRetentionInterval time.Duration `help:"Database deleted item retention interval" default:"4320h" env:"STDBDELETERETENTIONINTERVAL"` + GUIAddress string `name:"gui-address" help:"Override GUI address (e.g. \"http://192.0.2.42:8443\")" placeholder:"URL" env:"STGUIADDRESS"` + GUIAPIKey string `name:"gui-apikey" help:"Override GUI API key" placeholder:"API-KEY" env:"STGUIAPIKEY"` + LogFile string `name:"logfile" help:"Log file name (see below)" default:"${logFile}" placeholder:"PATH" env:"STLOGFILE"` + LogFlags int `name:"logflags" help:"Select information in log line prefix (see below)" default:"${logFlags}" placeholder:"BITS" env:"STLOGFLAGS"` + LogMaxFiles int `name:"log-max-old-files" help:"Number of old files to keep (zero to keep only current)" default:"${logMaxFiles}" placeholder:"N" env:"STLOGMAXOLDFILES"` + LogMaxSize int `help:"Maximum size of any file (zero to disable log rotation)" default:"${logMaxSize}" placeholder:"BYTES" env:"STLOGMAXSIZE"` + NoBrowser bool `help:"Do not start browser" env:"STNOBROWSER"` + NoPortProbing bool `help:"Don't try to find free ports for GUI and listen addresses on first startup" env:"STNOPORTPROBING"` + NoRestart bool `help:"Do not restart Syncthing when exiting due to API/GUI command, upgrade, or crash" env:"STNORESTART"` + NoUpgrade bool `help:"Disable automatic upgrades" env:"STNOUPGRADE"` + Paused bool `help:"Start with all devices and folders paused" env:"STPAUSED"` + Unpaused bool `help:"Start with all devices and folders unpaused" env:"STUNPAUSED"` + Verbose bool `help:"Print verbose log output" env:"STVERBOSE"` // Debug options below - DebugDBIndirectGCInterval time.Duration `env:"STGCINDIRECTEVERY" help:"Database indirection GC interval"` - DebugDBRecheckInterval time.Duration `env:"STRECHECKDBEVERY" help:"Database metadata recalculation interval"` - DebugGUIAssetsDir string `placeholder:"PATH" help:"Directory to load GUI assets from" env:"STGUIASSETS"` - DebugPerfStats bool `env:"STPERFSTATS" help:"Write running performance statistics to perf-$pid.csv (Unix only)"` - DebugProfileBlock bool `env:"STBLOCKPROFILE" help:"Write block profiles to block-$pid-$timestamp.pprof every 20 seconds"` - DebugProfileCPU bool `help:"Write a CPU profile to cpu-$pid.pprof on exit" env:"STCPUPROFILE"` - DebugProfileHeap bool `env:"STHEAPPROFILE" help:"Write heap profiles to heap-$pid-$timestamp.pprof each time heap usage increases"` - DebugProfilerListen string `placeholder:"ADDR" env:"STPROFILER" help:"Network profiler listen address"` - DebugResetDatabase bool `name:"reset-database" help:"Reset the database, forcing a full rescan and resync"` - DebugResetDeltaIdxs bool `name:"reset-deltas" help:"Reset delta index IDs, forcing a full index exchange"` + DebugGUIAssetsDir string `help:"Directory to load GUI assets from" placeholder:"PATH" env:"STGUIASSETS"` + DebugPerfStats bool `help:"Write running performance statistics to perf-$pid.csv (Unix only)" env:"STPERFSTATS"` + DebugProfileBlock bool `help:"Write block profiles to block-$pid-$timestamp.pprof every 20 seconds" env:"STBLOCKPROFILE"` + DebugProfileCPU bool `help:"Write a CPU profile to cpu-$pid.pprof on exit" env:"STCPUPROFILE"` + DebugProfileHeap bool `help:"Write heap profiles to heap-$pid-$timestamp.pprof each time heap usage increases" env:"STHEAPPROFILE"` + DebugProfilerListen string `help:"Network profiler listen address" placeholder:"ADDR" env:"STPROFILER" ` + DebugResetDeltaIdxs bool `help:"Reset delta index IDs, forcing a full index exchange"` // Internal options, not shown to users InternalRestarting bool `env:"STRESTART" hidden:"1"` @@ -206,31 +221,9 @@ func defaultVars() kong.Vars { } func main() { - // First some massaging of the raw command line to fit the new model. - // Basically this means adding the default command at the front, and - // converting -options to --options. - - args := os.Args[1:] - switch { - case len(args) == 0: - // Empty command line is equivalent to just calling serve - args = []string{"serve"} - case args[0] == "-help": - // For consistency, we consider this equivalent with --help even - // though kong would otherwise consider it a bad flag. - args[0] = "--help" - case args[0] == "-h", args[0] == "--help": - // Top level request for help, let it pass as-is to be handled by - // kong to list commands. - case strings.HasPrefix(args[0], "-"): - // There are flags not preceded by a command, so we tack on the - // "serve" command and convert the old style arguments (single dash) - // to new style (double dash). - args = append([]string{"serve"}, convertLegacyArgs(args)...) - } - // Create a parser with an overridden help function to print our extra // help info. + var entrypoint CLI parser, err := kong.New( &entrypoint, kong.ConfigureHelp(kong.HelpOptions{ @@ -245,7 +238,7 @@ func main() { } kongplete.Complete(parser) - ctx, err := parser.Parse(args) + ctx, err := parser.Parse(os.Args[1:]) parser.FatalIfErrorf(err) ctx.BindTo(l, (*logger.Logger)(nil)) // main logger available to subcommands err = ctx.Run() @@ -264,91 +257,44 @@ func helpHandler(options kong.HelpOptions, ctx *kong.Context) error { return nil } -// serveOptions.Run() is the entrypoint for `syncthing serve` -func (options serveOptions) Run() error { - l.SetFlags(options.LogFlags) +// serveCmd.Run() is the entrypoint for `syncthing serve` +func (c *serveCmd) Run() error { + l.SetFlags(c.LogFlags) - if options.GUIAddress != "" { + if c.GUIAddress != "" { // The config picks this up from the environment. - os.Setenv("STGUIADDRESS", options.GUIAddress) + os.Setenv("STGUIADDRESS", c.GUIAddress) } - if options.GUIAPIKey != "" { + if c.GUIAPIKey != "" { // The config picks this up from the environment. - os.Setenv("STGUIAPIKEY", options.GUIAPIKey) + os.Setenv("STGUIAPIKEY", c.GUIAPIKey) } - if options.HideConsole { + if c.HideConsole { osutil.HideConsole() } - // Not set as default above because the strings can be really long. - err := cmdutil.SetConfigDataLocationsFromFlags(options.HomeDir, options.ConfDir, options.DataDir) - if err != nil { - l.Warnln("Command line options:", err) - os.Exit(svcutil.ExitError.AsInt()) - } - // Treat an explicitly empty log file name as no log file - if options.LogFile == "" { - options.LogFile = "-" + if c.LogFile == "" { + c.LogFile = "-" } - if options.LogFile != "default" { + if c.LogFile != "default" { // We must set this *after* expandLocations above. - if err := locations.Set(locations.LogFile, options.LogFile); err != nil { + if err := locations.Set(locations.LogFile, c.LogFile); err != nil { l.Warnln("Setting log file path:", err) os.Exit(svcutil.ExitError.AsInt()) } } - if options.DebugGUIAssetsDir != "" { + if c.DebugGUIAssetsDir != "" { // The asset dir is blank if STGUIASSETS wasn't set, in which case we // should look for extra assets in the default place. - if err := locations.Set(locations.GUIAssets, options.DebugGUIAssetsDir); err != nil { + if err := locations.Set(locations.GUIAssets, c.DebugGUIAssetsDir); err != nil { l.Warnln("Setting GUI assets path:", err) os.Exit(svcutil.ExitError.AsInt()) } } - if options.Version { - fmt.Println(build.LongVersion) - return nil - } - - if options.Paths { - fmt.Print(locations.PrettyPaths()) - return nil - } - - if options.DeviceID { - cert, err := tls.LoadX509KeyPair( - locations.Get(locations.CertFile), - locations.Get(locations.KeyFile), - ) - if err != nil { - l.Warnln("Error reading device ID:", err) - os.Exit(svcutil.ExitError.AsInt()) - } - - fmt.Println(protocol.NewDeviceID(cert.Certificate[0])) - return nil - } - - if options.BrowserOnly { - if err := openGUI(); err != nil { - l.Warnln("Failed to open web UI:", err) - os.Exit(svcutil.ExitError.AsInt()) - } - return nil - } - - if options.GenerateDir != "" { - if err := generate.Generate(l, options.GenerateDir, "", "", options.NoDefaultFolder, options.SkipPortProbing); err != nil { - l.Warnln("Failed to generate config and keys:", err) - os.Exit(svcutil.ExitError.AsInt()) - } - return nil - } - // Ensure that our config and data directories exist. for _, loc := range []locations.BaseDirEnum{locations.ConfigBaseDir, locations.DataBaseDir} { if err := syncthing.EnsureDir(locations.GetBaseDir(loc), 0o700); err != nil { @@ -357,61 +303,10 @@ func (options serveOptions) Run() error { } } - if options.UpgradeTo != "" { - err := upgrade.ToURL(options.UpgradeTo) - if err != nil { - l.Warnln("Error while Upgrading:", err) - os.Exit(svcutil.ExitError.AsInt()) - } - l.Infoln("Upgraded from", options.UpgradeTo) - return nil - } - - if options.UpgradeCheck { - if _, err := checkUpgrade(); err != nil { - l.Warnln("Checking for upgrade:", err) - os.Exit(exitCodeForUpgrade(err)) - } - return nil - } - - if options.Upgrade { - release, err := checkUpgrade() - if err == nil { - lf := flock.New(locations.Get(locations.LockFile)) - locked, err := lf.TryLock() - if err != nil { - l.Warnln("Upgrade:", err) - os.Exit(1) - } else if locked { - err = upgradeViaRest() - } else { - err = upgrade.To(release) - } - _ = lf.Unlock() - _ = os.Remove(locations.Get(locations.LockFile)) - } - if err != nil { - l.Warnln("Upgrade:", err) - os.Exit(exitCodeForUpgrade(err)) - } - l.Infof("Upgraded to %q", release.Tag) - os.Exit(svcutil.ExitUpgrade.AsInt()) - } - - if options.DebugResetDatabase { - if err := resetDB(); err != nil { - l.Warnln("Resetting database:", err) - os.Exit(svcutil.ExitError.AsInt()) - } - l.Infoln("Successfully reset database - it will be rebuilt after next start.") - return nil - } - - if options.InternalInnerProcess { - syncthingMain(options) + if c.InternalInnerProcess { + c.syncthingMain() } else { - monitorMain(options) + c.monitorMain() } return nil } @@ -520,14 +415,14 @@ func upgradeViaRest() error { return err } -func syncthingMain(options serveOptions) { - if options.DebugProfileBlock { +func (c *serveCmd) syncthingMain() { + if c.DebugProfileBlock { startBlockProfiler() } - if options.DebugProfileHeap { + if c.DebugProfileHeap { startHeapProfiler() } - if options.DebugPerfStats { + if c.DebugPerfStats { startPerfStats() } @@ -572,7 +467,7 @@ func syncthingMain(options serveOptions) { evLogger := events.NewLogger() earlyService.Add(evLogger) - cfgWrapper, err := syncthing.LoadConfigAtStartup(locations.Get(locations.ConfigFile), cert, evLogger, options.AllowNewerConfig, options.NoDefaultFolder, options.SkipPortProbing) + cfgWrapper, err := syncthing.LoadConfigAtStartup(locations.Get(locations.ConfigFile), cert, evLogger, c.AllowNewerConfig, c.NoPortProbing) if err != nil { l.Warnln("Failed to initialize config:", err) os.Exit(svcutil.ExitError.AsInt()) @@ -584,7 +479,7 @@ func syncthingMain(options serveOptions) { // unless we are in a build where it's disabled or the STNOUPGRADE // environment variable is set. - if build.IsCandidate && !upgrade.DisabledByCompilation && !options.NoUpgrade { + if build.IsCandidate && !upgrade.DisabledByCompilation && !c.NoUpgrade { cfgWrapper.Modify(func(cfg *config.Configuration) { l.Infoln("Automatic upgrade is always enabled for candidate releases.") if cfg.Options.AutoUpgradeIntervalH == 0 || cfg.Options.AutoUpgradeIntervalH > 24 { @@ -597,8 +492,12 @@ func syncthingMain(options serveOptions) { }) } - dbFile := locations.Get(locations.Database) - ldb, err := syncthing.OpenDBBackend(dbFile, cfgWrapper.Options().DatabaseTuning) + if err := syncthing.TryMigrateDatabase(c.DBDeleteRetentionInterval); err != nil { + l.Warnln("Failed to migrate old-style database:", err) + os.Exit(1) + } + + sdb, err := syncthing.OpenDatabase(locations.Get(locations.Database), c.DBDeleteRetentionInterval) if err != nil { l.Warnln("Error opening database:", err) os.Exit(1) @@ -607,11 +506,11 @@ func syncthingMain(options serveOptions) { // Check if auto-upgrades is possible, and if yes, and it's enabled do an initial // upgrade immediately. The auto-upgrade routine can only be started // later after App is initialised. - - autoUpgradePossible := autoUpgradePossible(options) + autoUpgradePossible := c.autoUpgradePossible() if autoUpgradePossible && cfgWrapper.Options().AutoUpgradeEnabled() { // try to do upgrade directly and log the error if relevant. - release, err := initialAutoUpgradeCheck(db.NewMiscDataNamespace(ldb)) + miscDB := db.NewMiscDB(sdb) + release, err := initialAutoUpgradeCheck(miscDB) if err == nil { err = upgrade.To(release) } @@ -622,48 +521,40 @@ func syncthingMain(options serveOptions) { l.Infoln("Initial automatic upgrade:", err) } } else { - l.Infof("Upgraded to %q, exiting now.", release.Tag) + l.Infof("Upgraded to %q, should exit now.", release.Tag) os.Exit(svcutil.ExitUpgrade.AsInt()) } } - if options.Unpaused { + if c.Unpaused { setPauseState(cfgWrapper, false) - } else if options.Paused { + } else if c.Paused { setPauseState(cfgWrapper, true) } appOpts := syncthing.Options{ - NoUpgrade: options.NoUpgrade, - ProfilerAddr: options.DebugProfilerListen, - ResetDeltaIdxs: options.DebugResetDeltaIdxs, - Verbose: options.Verbose, - DBRecheckInterval: options.DebugDBRecheckInterval, - DBIndirectGCInterval: options.DebugDBIndirectGCInterval, + NoUpgrade: c.NoUpgrade, + ProfilerAddr: c.DebugProfilerListen, + ResetDeltaIdxs: c.DebugResetDeltaIdxs, + Verbose: c.Verbose, + DBMaintenanceInterval: c.DBMaintenanceInterval, } - if options.Audit || cfgWrapper.Options().AuditEnabled { + if c.Audit || cfgWrapper.Options().AuditEnabled { l.Infoln("Auditing is enabled.") auditFile := cfgWrapper.Options().AuditFile // Ignore config option if command-line option is set - if options.AuditFile != "" { + if c.AuditFile != "" { l.Debugln("Using the audit file from the command-line parameter.") - auditFile = options.AuditFile + auditFile = c.AuditFile } appOpts.AuditWriter = auditWriter(auditFile) } - if dur, err := time.ParseDuration(os.Getenv("STRECHECKDBEVERY")); err == nil { - appOpts.DBRecheckInterval = dur - } - if dur, err := time.ParseDuration(os.Getenv("STGCINDIRECTEVERY")); err == nil { - appOpts.DBIndirectGCInterval = dur - } - - app, err := syncthing.New(cfgWrapper, ldb, evLogger, cert, appOpts) + app, err := syncthing.New(cfgWrapper, sdb, evLogger, cert, appOpts) if err != nil { l.Warnln("Failed to start Syncthing:", err) os.Exit(svcutil.ExitError.AsInt()) @@ -675,7 +566,7 @@ func syncthingMain(options serveOptions) { setupSignalHandling(app) - if options.DebugProfileCPU { + if c.DebugProfileCPU { f, err := os.Create(fmt.Sprintf("cpu-%d.pprof", os.Getpid())) if err != nil { l.Warnln("Creating profile:", err) @@ -693,7 +584,7 @@ func syncthingMain(options serveOptions) { cleanConfigDirectory() - if cfgWrapper.Options().StartBrowser && !options.NoBrowser && !options.InternalRestarting { + if cfgWrapper.Options().StartBrowser && !c.NoBrowser && !c.InternalRestarting { // Can potentially block if the utility we are invoking doesn't // fork, and just execs, hence keep it in its own routine. go func() { _ = openURL(cfgWrapper.GUI().URL()) }() @@ -705,7 +596,7 @@ func syncthingMain(options serveOptions) { l.Warnln("Syncthing stopped with error:", app.Error()) } - if options.DebugProfileCPU { + if c.DebugProfileCPU { pprof.StopCPUProfile() } @@ -782,15 +673,11 @@ func auditWriter(auditFile string) io.Writer { return fd } -func resetDB() error { - return os.RemoveAll(locations.Get(locations.Database)) -} - -func autoUpgradePossible(options serveOptions) bool { +func (c *serveCmd) autoUpgradePossible() bool { if upgrade.DisabledByCompilation { return false } - if options.NoUpgrade { + if c.NoUpgrade { l.Infof("No automatic upgrades; STNOUPGRADE environment variable defined.") return false } @@ -854,7 +741,7 @@ func autoUpgrade(cfg config.Wrapper, app *syncthing.App, evLogger events.Logger) } } -func initialAutoUpgradeCheck(misc *db.NamespacedKV) (upgrade.Release, error) { +func initialAutoUpgradeCheck(misc *db.Typed) (upgrade.Release, error) { if last, ok, err := misc.Time(upgradeCheckKey); err == nil && ok && time.Since(last) < upgradeCheckInterval { return upgrade.Release{}, errTooEarlyUpgradeCheck } @@ -944,20 +831,165 @@ func exitCodeForUpgrade(err error) int { return svcutil.ExitError.AsInt() } -// convertLegacyArgs returns the slice of arguments with single dash long -// flags converted to double dash long flags. -func convertLegacyArgs(args []string) []string { - // Legacy args begin with a single dash, followed by two or more characters. - legacyExp := regexp.MustCompile(`^-\w{2,}`) +type versionCmd struct{} - res := make([]string, len(args)) - for i, arg := range args { - if legacyExp.MatchString(arg) { - res[i] = "-" + arg - } else { - res[i] = arg - } +func (versionCmd) Run() error { + fmt.Println(build.LongVersion) + return nil +} + +type deviceIDCmd struct{} + +func (deviceIDCmd) Run() error { + cert, err := tls.LoadX509KeyPair( + locations.Get(locations.CertFile), + locations.Get(locations.KeyFile), + ) + if err != nil { + l.Warnln("Error reading device ID:", err) + os.Exit(svcutil.ExitError.AsInt()) } - return res + fmt.Println(protocol.NewDeviceID(cert.Certificate[0])) + return nil +} + +type pathsCmd struct{} + +func (pathsCmd) Run() error { + fmt.Print(locations.PrettyPaths()) + return nil +} + +type upgradeCmd struct { + CheckOnly bool `short:"c" help:"Check for available upgrade, then exit"` + From string `short:"u" placeholder:"URL" help:"Force upgrade directly from specified URL"` +} + +func (u upgradeCmd) Run() error { + if u.CheckOnly { + if _, err := checkUpgrade(); err != nil { + l.Warnln("Checking for upgrade:", err) + os.Exit(exitCodeForUpgrade(err)) + } + return nil + } + + if u.From != "" { + err := upgrade.ToURL(u.From) + if err != nil { + l.Warnln("Error while Upgrading:", err) + os.Exit(svcutil.ExitError.AsInt()) + } + l.Infoln("Upgraded from", u.From) + return nil + } + + release, err := checkUpgrade() + if err == nil { + lf := flock.New(locations.Get(locations.LockFile)) + var locked bool + locked, err = lf.TryLock() + if err != nil { + l.Warnln("Upgrade:", err) + os.Exit(1) + } else if locked { + err = upgradeViaRest() + } else { + err = upgrade.To(release) + } + } + if err != nil { + l.Warnln("Upgrade:", err) + os.Exit(exitCodeForUpgrade(err)) + } + l.Infof("Upgraded to %q", release.Tag) + os.Exit(svcutil.ExitUpgrade.AsInt()) + return nil +} + +type browserCmd struct{} + +func (browserCmd) Run() error { + if err := openGUI(); err != nil { + l.Warnln("Failed to open web UI:", err) + os.Exit(svcutil.ExitError.AsInt()) + } + return nil +} + +type debugCmd struct { + ResetDatabase resetDatabaseCmd `cmd:"" help:"Reset the database, forcing a full rescan and resync"` + DatabaseStatistics databaseStatsCmd `cmd:"" help:"Display database size statistics"` +} + +type resetDatabaseCmd struct{} + +func (resetDatabaseCmd) Run() error { + l.Infoln("Removing database in", locations.Get(locations.Database)) + if err := os.RemoveAll(locations.Get(locations.Database)); err != nil { + l.Warnln("Resetting database:", err) + os.Exit(svcutil.ExitError.AsInt()) + } + l.Infoln("Successfully reset database - it will be rebuilt after next start.") + return nil +} + +type databaseStatsCmd struct{} + +func (c databaseStatsCmd) Run() error { + db, err := sqlite.Open(locations.Get(locations.Database)) + if err != nil { + return err + } + ds, err := db.Statistics() + if err != nil { + return err + } + + tw := tabwriter.NewWriter(os.Stdout, 2, 2, 2, ' ', 0) + hdr := fmt.Sprintf("%s\t%s\t%s\t%12s\t%7s\n", "DATABASE", "FOLDER ID", "TABLE", "SIZE", "FILL") + fmt.Fprint(tw, hdr) + fmt.Fprint(tw, regexp.MustCompile(`[A-Z]`).ReplaceAllString(hdr, "=")) + c.printStat(tw, ds) + return tw.Flush() +} + +func (c databaseStatsCmd) printStat(w io.Writer, s *sqlite.DatabaseStatistics) { + for _, table := range s.Tables { + fmt.Fprintf(w, "%s\t%s\t%s\t%8d KiB\t%5.01f %%\n", s.Name, cmp.Or(s.FolderID, "-"), table.Name, table.Size/1024, float64(table.Size-table.Unused)*100/float64(table.Size)) + } + for _, next := range s.Children { + c.printStat(w, &next) + s.Total.Size += next.Total.Size + s.Total.Unused += next.Total.Unused + } + + totalName := s.Name + if len(s.Children) > 0 { + totalName += " + children" + } + fmt.Fprintf(w, "%s\t%s\t%s\t%8d KiB\t%5.01f %%\n", totalName, cmp.Or(s.FolderID, "-"), "(total)", s.Total.Size/1024, float64(s.Total.Size-s.Total.Unused)*100/float64(s.Total.Size)) +} + +func setConfigDataLocationsFromFlags(homeDir, confDir, dataDir string) error { + homeSet := homeDir != "" + confSet := confDir != "" + dataSet := dataDir != "" + switch { + case dataSet != confSet: + return errors.New("either both or none of --config and --data must be given, use --home to set both at once") + case homeSet && dataSet: + return errors.New("--home must not be used together with --config and --data") + case homeSet: + confDir = homeDir + dataDir = homeDir + fallthrough + case dataSet: + if err := locations.SetBaseDir(locations.ConfigBaseDir, confDir); err != nil { + return err + } + return locations.SetBaseDir(locations.DataBaseDir, dataDir) + } + return nil } diff --git a/cmd/syncthing/monitor.go b/cmd/syncthing/monitor.go index 932ae1309..4d52dfe8d 100644 --- a/cmd/syncthing/monitor.go +++ b/cmd/syncthing/monitor.go @@ -43,7 +43,7 @@ const ( panicUploadNoticeWait = 10 * time.Second ) -func monitorMain(options serveOptions) { +func (c *serveCmd) monitorMain() { l.SetPrefix("[monitor] ") var dst io.Writer = os.Stdout @@ -58,8 +58,8 @@ func monitorMain(options serveOptions) { open := func(name string) (io.WriteCloser, error) { return newAutoclosedFile(name, logFileAutoCloseDelay, logFileMaxOpenTime) } - if options.LogMaxSize > 0 { - fileDst, err = newRotatedFile(logFile, open, int64(options.LogMaxSize), options.LogMaxFiles) + if c.LogMaxSize > 0 { + fileDst, err = newRotatedFile(logFile, open, int64(c.LogMaxSize), c.LogMaxFiles) } else { fileDst, err = open(logFile) } @@ -178,7 +178,7 @@ func monitorMain(options serveOptions) { if exiterr, ok := err.(*exec.ExitError); ok { exitCode := exiterr.ExitCode() - if stopped || options.NoRestart { + if stopped || c.NoRestart { os.Exit(exitCode) } if exitCode == svcutil.ExitUpgrade.AsInt() { @@ -192,7 +192,7 @@ func monitorMain(options serveOptions) { } } - if options.NoRestart { + if c.NoRestart { os.Exit(svcutil.ExitError.AsInt()) } diff --git a/cmd/syncthing/perfstats_unix.go b/cmd/syncthing/perfstats_unix.go index 6da5a2116..81cf2fe7a 100644 --- a/cmd/syncthing/perfstats_unix.go +++ b/cmd/syncthing/perfstats_unix.go @@ -16,7 +16,9 @@ import ( "syscall" "time" + "github.com/syncthing/syncthing/lib/locations" "github.com/syncthing/syncthing/lib/protocol" + "golang.org/x/exp/constraints" ) func startPerfStats() { @@ -29,37 +31,68 @@ func savePerfStats(file string) { panic(err) } - var prevUsage int64 - var prevTime int64 - var rusage syscall.Rusage - var memstats runtime.MemStats + var prevTime time.Time + var curRus, prevRus syscall.Rusage + var curMem, prevMem runtime.MemStats var prevIn, prevOut int64 t0 := time.Now() + syscall.Getrusage(syscall.RUSAGE_SELF, &prevRus) + runtime.ReadMemStats(&prevMem) + + fmt.Fprintf(fd, "TIME_S\tCPU_S\tHEAP_KIB\tRSS_KIB\tNETIN_KBPS\tNETOUT_KBPS\tDBSIZE_KIB\n") + for t := range time.NewTicker(250 * time.Millisecond).C { - if err := syscall.Getrusage(syscall.RUSAGE_SELF, &rusage); err != nil { - continue - } - - curTime := time.Now().UnixNano() - timeDiff := curTime - prevTime - curUsage := rusage.Utime.Nano() + rusage.Stime.Nano() - usageDiff := curUsage - prevUsage - cpuUsagePercent := 100 * float64(usageDiff) / float64(timeDiff) - prevTime = curTime - prevUsage = curUsage + syscall.Getrusage(syscall.RUSAGE_SELF, &curRus) + runtime.ReadMemStats(&curMem) in, out := protocol.TotalInOut() - var inRate, outRate float64 - if timeDiff > 0 { - inRate = float64(in-prevIn) / (float64(timeDiff) / 1e9) // bytes per second - outRate = float64(out-prevOut) / (float64(timeDiff) / 1e9) // bytes per second - } + timeDiff := t.Sub(prevTime) + + fmt.Fprintf(fd, "%.03f\t%f\t%d\t%d\t%.0f\t%.0f\t%d\n", + t.Sub(t0).Seconds(), + rate(cpusec(&prevRus), cpusec(&curRus), timeDiff, 1), + (curMem.Sys-curMem.HeapReleased)/1024, + curRus.Maxrss/1024, + rate(prevIn, in, timeDiff, 1e3), + rate(prevOut, out, timeDiff, 1e3), + dirsize(locations.Get(locations.Database))/1024, + ) + + prevTime = t + prevRus = curRus + prevMem = curMem prevIn, prevOut = in, out - - runtime.ReadMemStats(&memstats) - - startms := int(t.Sub(t0).Seconds() * 1000) - - fmt.Fprintf(fd, "%d\t%f\t%d\t%d\t%.0f\t%.0f\n", startms, cpuUsagePercent, memstats.Alloc, memstats.Sys-memstats.HeapReleased, inRate, outRate) } } + +func cpusec(r *syscall.Rusage) float64 { + return float64(r.Utime.Nano()+r.Stime.Nano()) / float64(time.Second) +} + +type number interface { + constraints.Float | constraints.Integer +} + +func rate[T number](prev, cur T, d time.Duration, div float64) float64 { + diff := cur - prev + rate := float64(diff) / d.Seconds() / div + return rate +} + +func dirsize(location string) int64 { + entries, err := os.ReadDir(location) + if err != nil { + return 0 + } + + var size int64 + for _, entry := range entries { + fi, err := entry.Info() + if err != nil { + continue + } + size += fi.Size() + } + + return size +} diff --git a/etc/linux-upstart/user/syncthing.conf b/etc/linux-upstart/user/syncthing.conf index 0ef9efa7f..fc973073f 100644 --- a/etc/linux-upstart/user/syncthing.conf +++ b/etc/linux-upstart/user/syncthing.conf @@ -18,4 +18,4 @@ env STNORESTART=yes respawn # the syncthing command Upstart is to execute when it is started up -exec $SYNCTHING_EXE -no-browser +exec $SYNCTHING_EXE --no-browser diff --git a/go.mod b/go.mod index ba312eb1d..8b483301c 100644 --- a/go.mod +++ b/go.mod @@ -10,19 +10,20 @@ require ( github.com/calmh/incontainer v1.0.0 github.com/calmh/xdr v1.2.0 github.com/ccding/go-stun v0.1.5 - github.com/chmduquesne/rollinghash v4.0.0+incompatible + github.com/coreos/go-semver v0.3.1 github.com/d4l3k/messagediff v1.2.1 github.com/getsentry/raven-go v0.2.0 github.com/go-ldap/ldap/v3 v3.4.11 github.com/gobwas/glob v0.2.3 github.com/gofrs/flock v0.12.1 - github.com/greatroar/blobloom v0.8.0 github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/jackpal/gateway v1.0.16 github.com/jackpal/go-nat-pmp v1.0.2 + github.com/jmoiron/sqlx v1.4.0 github.com/julienschmidt/httprouter v1.3.0 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/maruel/panicparse/v2 v2.5.0 + github.com/mattn/go-sqlite3 v1.14.28 github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2 github.com/maxmind/geoipupdate/v6 v6.1.0 github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75 @@ -42,12 +43,14 @@ require ( github.com/willabides/kongplete v0.4.0 go.uber.org/automaxprocs v1.6.0 golang.org/x/crypto v0.38.0 + golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 golang.org/x/net v0.40.0 golang.org/x/sys v0.33.0 golang.org/x/text v0.25.0 golang.org/x/time v0.11.0 golang.org/x/tools v0.33.0 google.golang.org/protobuf v1.36.6 + modernc.org/sqlite v1.37.0 sigs.k8s.io/yaml v1.4.0 ) @@ -59,9 +62,9 @@ require ( github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/coreos/go-semver v0.3.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/ebitengine/purego v0.8.3 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect @@ -74,7 +77,9 @@ require ( github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect github.com/nxadm/tail v1.4.11 // indirect github.com/onsi/ginkgo/v2 v2.23.4 // indirect github.com/oschwald/maxminddb-golang v1.13.1 // indirect @@ -85,6 +90,7 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/stretchr/objx v0.5.2 // indirect @@ -96,7 +102,13 @@ require ( golang.org/x/mod v0.24.0 // indirect golang.org/x/sync v0.14.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + modernc.org/libc v1.62.1 // indirect + modernc.org/mathutil v1.7.1 // indirect + modernc.org/memory v1.9.1 // indirect ) // https://github.com/gobwas/glob/pull/55 replace github.com/gobwas/glob v0.2.3 => github.com/calmh/glob v0.0.0-20220615080505-1d823af5017b + +// https://github.com/mattn/go-sqlite3/pull/1338 +replace github.com/mattn/go-sqlite3 v1.14.28 => github.com/calmh/go-sqlite3 v1.14.29-0.20250520105817-2e94cda3f7f8 diff --git a/go.sum b/go.sum index a552461c1..5f505877f 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/AudriusButkevicius/recli v0.0.7-0.20220911121932-d000ce8fbf0f h1:GmH5lT+moM7PbAJFBq57nH9WJ+wRnBXr/tyaYWbSAx8= github.com/AudriusButkevicius/recli v0.0.7-0.20220911121932-d000ce8fbf0f/go.mod h1:Nhfib1j/VFnLrXL9cHgA+/n2O6P5THuWelOnbfPNd78= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U= @@ -29,6 +31,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/calmh/glob v0.0.0-20220615080505-1d823af5017b h1:Fjm4GuJ+TGMgqfGHN42IQArJb77CfD/mAwLbDUoJe6g= github.com/calmh/glob v0.0.0-20220615080505-1d823af5017b/go.mod h1:91K7jfEsgJSyfSrX+gmrRfZMtntx6JsHolWubGXDopg= +github.com/calmh/go-sqlite3 v1.14.29-0.20250520105817-2e94cda3f7f8 h1:oNVrBJGXkD334ToEmxJz8G6LhzD1/sMA4twMHsMLzQo= +github.com/calmh/go-sqlite3 v1.14.29-0.20250520105817-2e94cda3f7f8/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/calmh/incontainer v1.0.0 h1:g2cTUtZuFGmMGX8GoykPkN1Judj2uw8/3/aEtq4Z/rg= github.com/calmh/incontainer v1.0.0/go.mod h1:eOhqnw15c9X+4RNBe0W3HlUZFfX16O0EDsCOInTndHY= github.com/calmh/xdr v1.2.0 h1:GaGSNH4ZDw9kNdYqle6+RcAENiaQ8/611Ok+jQbBEeU= @@ -41,8 +45,6 @@ github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d h1:S2NE3iHSwP0XV github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chmduquesne/rollinghash v4.0.0+incompatible h1:hnREQO+DXjqIw3rUTzWN7/+Dpw+N5Um8zpKV0JOEgbo= -github.com/chmduquesne/rollinghash v4.0.0+incompatible/go.mod h1:Uc2I36RRfTAf7Dge82bi3RU0OQUmXT9iweIcPqvr8A0= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -55,6 +57,8 @@ github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkE github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/ebitengine/purego v0.8.3 h1:K+0AjQp63JEZTEMZiwsI9g0+hAMNohwUOtY0RPGexmc= github.com/ebitengine/purego v0.8.3/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -74,6 +78,8 @@ github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ4 github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= @@ -105,8 +111,6 @@ github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 h1:gD0vax+4I+mAj+jECh github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/greatroar/blobloom v0.8.0 h1:I9RlEkfqK9/6f1v9mFmDYegDQ/x0mISCpiNpAm23Pt4= -github.com/greatroar/blobloom v0.8.0/go.mod h1:mjMJ1hh1wjGVfr93QIHJ6FfDNVrA0IELv8OvMHJxHKs= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -141,6 +145,8 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= +github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= @@ -153,10 +159,15 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0= github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= github.com/maruel/panicparse/v2 v2.5.0 h1:yCtuS0FWjfd0RTYMXGpDvWcb0kINm8xJGu18/xMUh00= github.com/maruel/panicparse/v2 v2.5.0/go.mod h1:DA2fDiBk63bKfBf4CVZP9gb4fuvzdPbLDsSI873hweQ= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2 h1:yVCLo4+ACVroOEr4iFU1iH46Ldlzz2rTuu18Ra7M8sU= github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2/go.mod h1:VzB2VoMh1Y32/QqDfg9ZJYHj99oM4LiGtqPZydTiQSQ= github.com/maxmind/geoipupdate/v6 v6.1.0 h1:sdtTHzzQNJlXF5+fd/EoPTucRHyMonYt/Cok8xzzfqA= @@ -165,6 +176,8 @@ github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75 h1:cUVxyR+U github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75/go.mod h1:pBbZyGwC5i16IBkjVKoy/sznA8jPD/K9iedwe1ESE6w= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= @@ -218,6 +231,8 @@ github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzuk github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o= github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 h1:bsUq1dX0N8AOIL7EB/X911+m4EHsnWEHeJ0c+3TTBrg= github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab h1:ZjX6I48eZSFetPb41dHudEyVr5v953N15TsNZXlkcWY= github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab/go.mod h1:/PfPXh0EntGc3QAAyUaviy4S9tzy4Zp0e2ilq4voC6E= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= @@ -273,6 +288,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= +golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= @@ -312,6 +329,7 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -359,5 +377,29 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +modernc.org/cc/v4 v4.25.2 h1:T2oH7sZdGvTaie0BRNFbIYsabzCxUQg8nLqCdQ2i0ic= +modernc.org/cc/v4 v4.25.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= +modernc.org/ccgo/v4 v4.25.1 h1:TFSzPrAGmDsdnhT9X2UrcPMI3N/mJ9/X9ykKXwLhDsU= +modernc.org/ccgo/v4 v4.25.1/go.mod h1:njjuAYiPflywOOrm3B7kCB444ONP5pAVr8PIEoE0uDw= +modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= +modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= +modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= +modernc.org/libc v1.62.1 h1:s0+fv5E3FymN8eJVmnk0llBe6rOxCu/DEU+XygRbS8s= +modernc.org/libc v1.62.1/go.mod h1:iXhATfJQLjG3NWy56a6WVU73lWOcdYVxsvwCgoPljuo= +modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= +modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= +modernc.org/memory v1.9.1 h1:V/Z1solwAVmMW1yttq3nDdZPJqV1rM05Ccq6KMSZ34g= +modernc.org/memory v1.9.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= +modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= +modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= +modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= +modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= +modernc.org/sqlite v1.37.0 h1:s1TMe7T3Q3ovQiK2Ouz4Jwh7dw4ZDqbebSDTlSJdfjI= +modernc.org/sqlite v1.37.0/go.mod h1:5YiWv+YviqGMuGw4V+PNplcyaJ5v+vQd7TQOgkACoJM= +modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= +modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/gui/default/syncthing/core/aboutModalView.html b/gui/default/syncthing/core/aboutModalView.html index 4cf96efe7..3d2240c4f 100644 --- a/gui/default/syncthing/core/aboutModalView.html +++ b/gui/default/syncthing/core/aboutModalView.html @@ -30,7 +30,7 @@

The Syncthing Authors

-Jakob Borg, Audrius Butkevicius, Jesse Lucas, Simon Frei, Tomasz Wilczyński, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Emil Lundberg, Eric P, Evgeny Kuznetsov, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ross Smith II, Ryan Sullivan, Sergey Mishin, Stefan Tatschner, Wulf Weich, bt90, greatroar, Aaron Bieber, Adam Piggott, Adel Qalieh, Alan Pope, Alberto Donato, Aleksey Vasenev, Alessandro G., Alex Ionescu, Alex Lindeman, Alex Xu, Alexander Seiler, Alexandre Alves, Aman Gupta, Anatoli Babenia, Andreas Sommer, Andrew Dunham, Andrew Meyer, Andrew Rabert, Andrey D, Anjan Momi, Anthony Goeckner, Antoine Lamielle, Anur, Aranjedeath, Arkadiusz Tymiński, Aroun, Arthur Axel fREW Schmidt, Artur Zubilewicz, Ashish Bhate, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Beat Reichenbach, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benedikt Morbach, Benjamin Nater, Benno Fünfstück, Benny Ng, Boqin Qin, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Catfriend1, Cathryne Linenweaver, Cedric Staniewski, Chih-Hsuan Yen, Choongkyu, Chris Howie, Chris Joel, Chris Tonkinson, Christian Kujau, Christian Prescott, Colin Kennedy, Cromefire_, Cyprien Devillez, Dale Visser, Dan, Daniel Barczyk, Daniel Bergmann, Daniel Martí, Daniel Padrta, Darshil Chanpura, David Rimmer, DeflateAwning, Denis A., Dennis Wilson, DerRockWolf, Devon G. Redekopp, Dimitri Papadopoulos Orfanos, Dmitry Saveliev, Domenic Horner, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Eng Zer Jun, Eric Lesiuta, Erik Meitner, Evan Spensley, Federico Castagnini, Felix, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gahl Saraf, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Greg, Gusted, Han Boetes, HansK-p, Harrison Jones, Hazem Krimi, Heiko Zuerker, Hireworks, Hugo Locurcio, Iain Barnett, Ian Johnson, Ikko Ashimine, Ilya Brin, Iskander Sharipov, Jaakko Hannikainen, Jacek Szafarkiewicz, Jack Croft, Jacob, Jake Peterson, James O'Beirne, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jaspitta, Jauder Ho, Jaya Chithra, Jaya Kumar, Jeffery To, Jens Diemer, Jerry Jacobs, Jochen Voss, Johan Andersson, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jonathan Cross, Jonta, Jose Manuel Delicado, Julian Lehrhuber, Jörg Thalheim, Jędrzej Kula, K.B.Dharun Krishna, Kalle Laine, Kapil Sareen, Karol Różycki, Kebin Liu, Keith Harrison, Keith Turner, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., Kurt Fitzner, LSmithx2, Lars Lehtonen, Laurent Arnoud, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Lukas Lihotzki, Luke Hamburg, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcel Meyer, Marcin Dziadus, Marcus B Spencer, Marcus Legendre, Mario Majila, Mark Pulford, Martchus, Martin Polehla, Mateusz Naściszewski, Mateusz Ż, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, Maurizio Tomasi, Max, Max Schulze, MaximAL, Maxime Thirouin, Maximilian, MichaIng, Michael Jephcote, Michael Rienstra, Michael Tilli, Migelo, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, Naveen, Nicholas Rishel, Nick Busey, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, Otiel, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Paul Donald, Pawel Palenica, Paweł Rozlach, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Phill Luby, Pier Paolo Ramon, Piotr Bejda, Pramodh KP, Quentin Hibon, Rahmi Pruitt, Richard Hartmann, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, Ruslan Yevdokymov, Ryan Qian, Sacheendra Talluri, Scott Klupfel, Sertonix, Severin von Wnuck-Lipinski, Shaarad Dalvi, Simon Mwepu, Simon Pickup, Sly_tom_cat, Sonu Kumar Saw, Stefan Kuntz, Steven Eckhoff, Suhas Gundimeda, Sven Bachmann, Sébastien WENSKE, Taylor Khan, Terrance, TheCreeper, Thomas, Thomas Hipp, Tim Abell, Tim Howes, Tim Nordenfur, Tobias Frölich, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tommy Thorn, Tommy van der Vorst, Tully Robinson, Tyler Brazier, Tyler Kropp, Unrud, Veeti Paananen, Victor Buinsky, Vik, Vil Brekin, Vladimir Rusinov, WangXi, Will Rouesnel, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, boomsquared, chenrui, chucic, cjc7373, cui fliter, d-volution, dashangcun, derekriemer, desbma, diemade, digital, domain, entity0xfe, georgespatton, ghjklw, guangwu, gudvinr, ignacy123, janost, jaseg, jelle van der Waa, jtagcat, klemens, kylosus, luchenhan, luzpaz, marco-m, mathias4833, maxice8, mclang, mv1005, nf, orangekame3, otbutz, overkill, perewa, polyfloyd, pullmerge, red_led, rubenbe, sec65, vapatel2, villekalliomaki, wangguoliang, wouter bolsterlee, xarx00, xjtdy888, 佛跳墙, 落心 +Jakob Borg, Audrius Butkevicius, Simon Frei, Tomasz Wilczyński, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, bt90, Caleb Callaway, Daniel Harte, Emil Lundberg, Eric P, Evgeny Kuznetsov, greatroar, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Ross Smith II, Stefan Tatschner, Wulf Weich, Adam Piggott, Adel Qalieh, Aleksey Vasenev, Alessandro G., Alex Ionescu, Alex Lindeman, Alex Xu, Alexander Seiler, Alexandre Alves, Aman Gupta, Andreas Sommer, andresvia, Andrew Rabert, Andrey D, andyleap, Anjan Momi, Anthony Goeckner, Antoine Lamielle, Anur, Aranjedeath, Arkadiusz Tymiński, Aroun, Arthur Axel fREW Schmidt, Artur Zubilewicz, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Beat Reichenbach, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benno Fünfstück, Benny Ng, boomsquared, Boqin Qin, Boris Rybalkin, Brendan Long, Catfriend1, Cathryne Linenweaver, Cedric Staniewski, Chih-Hsuan Yen, Choongkyu, Chris Howie, Chris Joel, Christian Kujau, Christian Prescott, chucic, cjc7373, Colin Kennedy, Cromefire_, Cyprien Devillez, d-volution, Dan, Daniel Barczyk, Daniel Bergmann, Daniel Martí, Daniel Padrta, Darshil Chanpura, dashangcun, David Rimmer, DeflateAwning, Denis A., Dennis Wilson, derekriemer, DerRockWolf, desbma, Devon G. Redekopp, digital, Dimitri Papadopoulos Orfanos, Dmitry Saveliev, domain, Domenic Horner, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Eng Zer Jun, entity0xfe, Eric Lesiuta, Erik Meitner, Evan Spensley, Federico Castagnini, Felix, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gahl Saraf, georgespatton, ghjklw, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Greg, guangwu, gudvinr, Gusted, Han Boetes, HansK-p, Harrison Jones, Hazem Krimi, Heiko Zuerker, Hireworks, Hugo Locurcio, Iain Barnett, Ian Johnson, ignacy123, Iskander Sharipov, Jaakko Hannikainen, Jack Croft, Jacob, Jake Peterson, James O'Beirne, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jaspitta, Jaya Chithra, Jaya Kumar, Jeffery To, jelle van der Waa, Jens Diemer, Jochen Voss, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jose Manuel Delicado, jtagcat, Julian Lehrhuber, Jörg Thalheim, Jędrzej Kula, Kapil Sareen, Karol Różycki, Kebin Liu, Keith Harrison, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., klemens, Kurt Fitzner, kylosus, Lars Lehtonen, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, LSmithx2, Lukas Lihotzki, Luke Hamburg, luzpaz, Majed Abdulaziz, Marc Laporte, Marcin Dziadus, Marcus B Spencer, Marcus Legendre, Mario Majila, Mark Pulford, Martchus, Mateusz Naściszewski, Mateusz Ż, mathias4833, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, Maurizio Tomasi, Max, Max Schulze, MaximAL, Maximilian, Michael Jephcote, Michael Rienstra, MichaIng, Migelo, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, mv1005, Nate Morrison, nf, Nicholas Rishel, Nick Busey, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, orangekame3, otbutz, overkill, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Paul Donald, Pawel Palenica, perewa, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Philippe Schommers, Phill Luby, Piotr Bejda, polyfloyd, Quentin Hibon, Rahmi Pruitt, red_led, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, rubenbe, Ruslan Yevdokymov, Ryan Qian, Ryan Sullivan, Sacheendra Talluri, Scott Klupfel, sec65, Sergey Mishin, Sertonix, Severin von Wnuck-Lipinski, Shaarad Dalvi, Simon Mwepu, Simon Pickup, Sly_tom_cat, Sonu Kumar Saw, Stefan Kuntz, Steven Eckhoff, Suhas Gundimeda, Sven Bachmann, Sébastien WENSKE, Taylor Khan, Terrance, TheCreeper, Thomas, Thomas Hipp, Tim Abell, Tim Howes, Tobias Frölich, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tommy van der Vorst, Tully Robinson, Tyler Brazier, Tyler Kropp, Unrud, vapatel2, Veeti Paananen, Victor Buinsky, Vik, Vil Brekin, villekalliomaki, Vladimir Rusinov, wangguoliang, WangXi, Will Rouesnel, William A. Kennington III, wouter bolsterlee, xarx00, Xavier O., xjtdy888, Yannic A., 佛跳墙, 落心
diff --git a/internal/db/counts.go b/internal/db/counts.go new file mode 100644 index 000000000..a24ecdb2f --- /dev/null +++ b/internal/db/counts.go @@ -0,0 +1,73 @@ +// Copyright (C) 2014 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package db + +import ( + "fmt" + "strings" + + "github.com/syncthing/syncthing/lib/protocol" +) + +type Counts struct { + Files int + Directories int + Symlinks int + Deleted int + Bytes int64 + Sequence int64 // zero for the global state + DeviceID protocol.DeviceID // device ID for remote devices, or special values for local/global + LocalFlags uint32 // the local flag for this count bucket +} + +func (c Counts) Add(other Counts) Counts { + return Counts{ + Files: c.Files + other.Files, + Directories: c.Directories + other.Directories, + Symlinks: c.Symlinks + other.Symlinks, + Deleted: c.Deleted + other.Deleted, + Bytes: c.Bytes + other.Bytes, + Sequence: c.Sequence + other.Sequence, + DeviceID: protocol.EmptyDeviceID, + LocalFlags: c.LocalFlags | other.LocalFlags, + } +} + +func (c Counts) TotalItems() int { + return c.Files + c.Directories + c.Symlinks + c.Deleted +} + +func (c Counts) String() string { + var flags strings.Builder + if c.LocalFlags&protocol.FlagLocalNeeded != 0 { + flags.WriteString("Need") + } + if c.LocalFlags&protocol.FlagLocalIgnored != 0 { + flags.WriteString("Ignored") + } + if c.LocalFlags&protocol.FlagLocalMustRescan != 0 { + flags.WriteString("Rescan") + } + if c.LocalFlags&protocol.FlagLocalReceiveOnly != 0 { + flags.WriteString("Recvonly") + } + if c.LocalFlags&protocol.FlagLocalUnsupported != 0 { + flags.WriteString("Unsupported") + } + if c.LocalFlags != 0 { + flags.WriteString(fmt.Sprintf("(%x)", c.LocalFlags)) + } + if flags.Len() == 0 { + flags.WriteString("---") + } + return fmt.Sprintf("{Device:%v, Files:%d, Dirs:%d, Symlinks:%d, Del:%d, Bytes:%d, Seq:%d, Flags:%s}", c.DeviceID, c.Files, c.Directories, c.Symlinks, c.Deleted, c.Bytes, c.Sequence, flags.String()) +} + +// Equal compares the numbers only, not sequence/dev/flags. +func (c Counts) Equal(o Counts) bool { + return c.Files == o.Files && c.Directories == o.Directories && c.Symlinks == o.Symlinks && c.Deleted == o.Deleted && c.Bytes == o.Bytes +} diff --git a/internal/db/interface.go b/internal/db/interface.go new file mode 100644 index 000000000..2fc43ec23 --- /dev/null +++ b/internal/db/interface.go @@ -0,0 +1,123 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package db // import "github.com/syncthing/syncthing/internal/db/sqlite" + +import ( + "iter" + "time" + + "github.com/syncthing/syncthing/lib/config" + "github.com/syncthing/syncthing/lib/protocol" + "github.com/thejerf/suture/v4" +) + +type DB interface { + Service(maintenanceInterval time.Duration) suture.Service + + // Basics + Update(folder string, device protocol.DeviceID, fs []protocol.FileInfo) error + Close() error + + // Single files + GetDeviceFile(folder string, device protocol.DeviceID, file string) (protocol.FileInfo, bool, error) + GetGlobalAvailability(folder, file string) ([]protocol.DeviceID, error) + GetGlobalFile(folder string, file string) (protocol.FileInfo, bool, error) + + // File iterators + // + // n.b. there is a slight inconsistency in the return types where some + // return a FileInfo iterator and some a FileMetadata iterator. The + // latter is more lightweight, and the discrepancy depends on how the + // functions tend to be used. We can introduce more variations as + // required. + AllGlobalFiles(folder string) (iter.Seq[FileMetadata], func() error) + AllGlobalFilesPrefix(folder string, prefix string) (iter.Seq[FileMetadata], func() error) + AllLocalFiles(folder string, device protocol.DeviceID) (iter.Seq[protocol.FileInfo], func() error) + AllLocalFilesBySequence(folder string, device protocol.DeviceID, startSeq int64, limit int) (iter.Seq[protocol.FileInfo], func() error) + AllLocalFilesWithPrefix(folder string, device protocol.DeviceID, prefix string) (iter.Seq[protocol.FileInfo], func() error) + AllLocalFilesWithBlocksHash(folder string, h []byte) (iter.Seq[FileMetadata], func() error) + AllNeededGlobalFiles(folder string, device protocol.DeviceID, order config.PullOrder, limit, offset int) (iter.Seq[protocol.FileInfo], func() error) + AllLocalBlocksWithHash(folder string, hash []byte) (iter.Seq[BlockMapEntry], func() error) + + // Cleanup + DropAllFiles(folder string, device protocol.DeviceID) error + DropDevice(device protocol.DeviceID) error + DropFilesNamed(folder string, device protocol.DeviceID, names []string) error + DropFolder(folder string) error + + // Various metadata + GetDeviceSequence(folder string, device protocol.DeviceID) (int64, error) + ListFolders() ([]string, error) + ListDevicesForFolder(folder string) ([]protocol.DeviceID, error) + RemoteSequences(folder string) (map[protocol.DeviceID]int64, error) + + // Counts + CountGlobal(folder string) (Counts, error) + CountLocal(folder string, device protocol.DeviceID) (Counts, error) + CountNeed(folder string, device protocol.DeviceID) (Counts, error) + CountReceiveOnlyChanged(folder string) (Counts, error) + + // Index IDs + DropAllIndexIDs() error + GetIndexID(folder string, device protocol.DeviceID) (protocol.IndexID, error) + SetIndexID(folder string, device protocol.DeviceID, id protocol.IndexID) error + + // MtimeFS + DeleteMtime(folder, name string) error + GetMtime(folder, name string) (ondisk, virtual time.Time) + PutMtime(folder, name string, ondisk, virtual time.Time) error + + KV +} + +// Generic KV store +type KV interface { + GetKV(key string) ([]byte, error) + PutKV(key string, val []byte) error + DeleteKV(key string) error + PrefixKV(prefix string) (iter.Seq[KeyValue], func() error) +} + +type BlockMapEntry struct { + BlocklistHash []byte + Offset int64 + BlockIndex int + Size int + FileName string +} + +type KeyValue struct { + Key string + Value []byte +} + +type FileMetadata struct { + Name string + Sequence int64 + ModNanos int64 + Size int64 + LocalFlags int64 + Type protocol.FileInfoType + Deleted bool + Invalid bool +} + +func (f *FileMetadata) ModTime() time.Time { + return time.Unix(0, f.ModNanos) +} + +func (f *FileMetadata) IsReceiveOnlyChanged() bool { + return f.LocalFlags&protocol.FlagLocalReceiveOnly != 0 +} + +func (f *FileMetadata) IsDirectory() bool { + return f.Type == protocol.FileInfoTypeDirectory +} + +func (f *FileMetadata) ShouldConflict() bool { + return f.LocalFlags&protocol.LocalConflictFlags != 0 +} diff --git a/internal/db/metrics.go b/internal/db/metrics.go new file mode 100644 index 000000000..abb350af3 --- /dev/null +++ b/internal/db/metrics.go @@ -0,0 +1,225 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package db + +import ( + "iter" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/syncthing/syncthing/lib/config" + "github.com/syncthing/syncthing/lib/protocol" +) + +var ( + metricCurrentOperations = promauto.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: "syncthing", + Subsystem: "db", + Name: "operations_current", + Help: "Number of database operations currently ongoing, per folder and operation", + }, []string{"folder", "operation"}) + metricTotalOperationSeconds = promauto.NewCounterVec(prometheus.CounterOpts{ + Namespace: "syncthing", + Subsystem: "db", + Name: "operation_seconds_total", + Help: "Total time spent in database operations, per folder and operation", + }, []string{"folder", "operation"}) + metricTotalOperationsCount = promauto.NewCounterVec(prometheus.CounterOpts{ + Namespace: "syncthing", + Subsystem: "db", + Name: "operations_total", + Help: "Total number of database operations, per folder and operation", + }, []string{"folder", "operation"}) + metricTotalFilesUpdatedCount = promauto.NewCounterVec(prometheus.CounterOpts{ + Namespace: "syncthing", + Subsystem: "db", + Name: "files_updated_total", + Help: "Total number of files updated", + }, []string{"folder"}) +) + +func MetricsWrap(db DB) DB { + return metricsDB{db} +} + +type metricsDB struct { + DB +} + +func (m metricsDB) account(folder, op string) func() { + t0 := time.Now() + metricCurrentOperations.WithLabelValues(folder, op).Inc() + return func() { + if dur := time.Since(t0).Seconds(); dur > 0 { + metricTotalOperationSeconds.WithLabelValues(folder, op).Add(dur) + } + metricTotalOperationsCount.WithLabelValues(folder, op).Inc() + metricCurrentOperations.WithLabelValues(folder, op).Dec() + } +} + +func (m metricsDB) AllLocalFilesWithBlocksHash(folder string, h []byte) (iter.Seq[FileMetadata], func() error) { + defer m.account(folder, "AllLocalFilesWithBlocksHash")() + return m.DB.AllLocalFilesWithBlocksHash(folder, h) +} + +func (m metricsDB) AllGlobalFiles(folder string) (iter.Seq[FileMetadata], func() error) { + defer m.account(folder, "AllGlobalFiles")() + return m.DB.AllGlobalFiles(folder) +} + +func (m metricsDB) AllGlobalFilesPrefix(folder string, prefix string) (iter.Seq[FileMetadata], func() error) { + defer m.account(folder, "AllGlobalFilesPrefix")() + return m.DB.AllGlobalFilesPrefix(folder, prefix) +} + +func (m metricsDB) AllLocalFiles(folder string, device protocol.DeviceID) (iter.Seq[protocol.FileInfo], func() error) { + defer m.account(folder, "AllLocalFiles")() + return m.DB.AllLocalFiles(folder, device) +} + +func (m metricsDB) AllLocalFilesWithPrefix(folder string, device protocol.DeviceID, prefix string) (iter.Seq[protocol.FileInfo], func() error) { + defer m.account(folder, "AllLocalFilesPrefix")() + return m.DB.AllLocalFilesWithPrefix(folder, device, prefix) +} + +func (m metricsDB) AllLocalFilesBySequence(folder string, device protocol.DeviceID, startSeq int64, limit int) (iter.Seq[protocol.FileInfo], func() error) { + defer m.account(folder, "AllLocalFilesBySequence")() + return m.DB.AllLocalFilesBySequence(folder, device, startSeq, limit) +} + +func (m metricsDB) AllNeededGlobalFiles(folder string, device protocol.DeviceID, order config.PullOrder, limit, offset int) (iter.Seq[protocol.FileInfo], func() error) { + defer m.account(folder, "AllNeededGlobalFiles")() + return m.DB.AllNeededGlobalFiles(folder, device, order, limit, offset) +} + +func (m metricsDB) GetGlobalAvailability(folder, file string) ([]protocol.DeviceID, error) { + defer m.account(folder, "GetGlobalAvailability")() + return m.DB.GetGlobalAvailability(folder, file) +} + +func (m metricsDB) AllLocalBlocksWithHash(folder string, hash []byte) (iter.Seq[BlockMapEntry], func() error) { + defer m.account("-", "AllLocalBlocksWithHash")() + return m.DB.AllLocalBlocksWithHash(folder, hash) +} + +func (m metricsDB) Close() error { + defer m.account("-", "Close")() + return m.DB.Close() +} + +func (m metricsDB) ListDevicesForFolder(folder string) ([]protocol.DeviceID, error) { + defer m.account(folder, "ListDevicesForFolder")() + return m.DB.ListDevicesForFolder(folder) +} + +func (m metricsDB) RemoteSequences(folder string) (map[protocol.DeviceID]int64, error) { + defer m.account(folder, "RemoteSequences")() + return m.DB.RemoteSequences(folder) +} + +func (m metricsDB) DropAllFiles(folder string, device protocol.DeviceID) error { + defer m.account(folder, "DropAllFiles")() + return m.DB.DropAllFiles(folder, device) +} + +func (m metricsDB) DropDevice(device protocol.DeviceID) error { + defer m.account("-", "DropDevice")() + return m.DB.DropDevice(device) +} + +func (m metricsDB) DropFilesNamed(folder string, device protocol.DeviceID, names []string) error { + defer m.account(folder, "DropFilesNamed")() + return m.DB.DropFilesNamed(folder, device, names) +} + +func (m metricsDB) DropFolder(folder string) error { + defer m.account(folder, "DropFolder")() + return m.DB.DropFolder(folder) +} + +func (m metricsDB) DropAllIndexIDs() error { + defer m.account("-", "IndexIDDropAll")() + return m.DB.DropAllIndexIDs() +} + +func (m metricsDB) ListFolders() ([]string, error) { + defer m.account("-", "ListFolders")() + return m.DB.ListFolders() +} + +func (m metricsDB) GetGlobalFile(folder string, file string) (protocol.FileInfo, bool, error) { + defer m.account(folder, "GetGlobalFile")() + return m.DB.GetGlobalFile(folder, file) +} + +func (m metricsDB) CountGlobal(folder string) (Counts, error) { + defer m.account(folder, "CountGlobal")() + return m.DB.CountGlobal(folder) +} + +func (m metricsDB) GetIndexID(folder string, device protocol.DeviceID) (protocol.IndexID, error) { + defer m.account(folder, "IndexIDGet")() + return m.DB.GetIndexID(folder, device) +} + +func (m metricsDB) GetDeviceFile(folder string, device protocol.DeviceID, file string) (protocol.FileInfo, bool, error) { + defer m.account(folder, "GetDeviceFile")() + return m.DB.GetDeviceFile(folder, device, file) +} + +func (m metricsDB) CountLocal(folder string, device protocol.DeviceID) (Counts, error) { + defer m.account(folder, "CountLocal")() + return m.DB.CountLocal(folder, device) +} + +func (m metricsDB) CountNeed(folder string, device protocol.DeviceID) (Counts, error) { + defer m.account(folder, "CountNeed")() + return m.DB.CountNeed(folder, device) +} + +func (m metricsDB) CountReceiveOnlyChanged(folder string) (Counts, error) { + defer m.account(folder, "CountReceiveOnlyChanged")() + return m.DB.CountReceiveOnlyChanged(folder) +} + +func (m metricsDB) GetDeviceSequence(folder string, device protocol.DeviceID) (int64, error) { + defer m.account(folder, "GetDeviceSequence")() + return m.DB.GetDeviceSequence(folder, device) +} + +func (m metricsDB) SetIndexID(folder string, device protocol.DeviceID, id protocol.IndexID) error { + defer m.account(folder, "IndexIDSet")() + return m.DB.SetIndexID(folder, device, id) +} + +func (m metricsDB) Update(folder string, device protocol.DeviceID, fs []protocol.FileInfo) error { + defer m.account(folder, "Update")() + defer metricTotalFilesUpdatedCount.WithLabelValues(folder).Add(float64(len(fs))) + return m.DB.Update(folder, device, fs) +} + +func (m metricsDB) GetKV(key string) ([]byte, error) { + defer m.account("-", "GetKV")() + return m.DB.GetKV(key) +} + +func (m metricsDB) PutKV(key string, val []byte) error { + defer m.account("-", "PutKV")() + return m.DB.PutKV(key, val) +} + +func (m metricsDB) DeleteKV(key string) error { + defer m.account("-", "DeleteKV")() + return m.DB.DeleteKV(key) +} + +func (m metricsDB) PrefixKV(prefix string) (iter.Seq[KeyValue], func() error) { + defer m.account("-", "PrefixKV")() + return m.DB.PrefixKV(prefix) +} diff --git a/lib/db/observed.go b/internal/db/observed.go similarity index 55% rename from lib/db/observed.go rename to internal/db/observed.go index 47705b613..04aa64f4a 100644 --- a/lib/db/observed.go +++ b/internal/db/observed.go @@ -8,6 +8,7 @@ package db import ( "fmt" + "strings" "time" "google.golang.org/protobuf/proto" @@ -17,6 +18,14 @@ import ( "github.com/syncthing/syncthing/lib/protocol" ) +type ObservedDB struct { + kv KV +} + +func NewObservedDB(kv KV) *ObservedDB { + return &ObservedDB{kv: kv} +} + type ObservedFolder struct { Time time.Time `json:"time"` Label string `json:"label"` @@ -52,39 +61,42 @@ func (o *ObservedDevice) fromWire(w *dbproto.ObservedDevice) { o.Address = w.GetAddress() } -func (db *Lowlevel) AddOrUpdatePendingDevice(device protocol.DeviceID, name, address string) error { - key := db.keyer.GeneratePendingDeviceKey(nil, device[:]) +func (db *ObservedDB) AddOrUpdatePendingDevice(device protocol.DeviceID, name, address string) error { + key := "device/" + device.String() od := &dbproto.ObservedDevice{ Time: timestamppb.New(time.Now().Truncate(time.Second)), Name: name, Address: address, } - return db.Put(key, mustMarshal(od)) + return db.kv.PutKV(key, mustMarshal(od)) } -func (db *Lowlevel) RemovePendingDevice(device protocol.DeviceID) error { - key := db.keyer.GeneratePendingDeviceKey(nil, device[:]) - return db.Delete(key) +func (db *ObservedDB) RemovePendingDevice(device protocol.DeviceID) error { + key := "device/" + device.String() + return db.kv.DeleteKV(key) } // PendingDevices enumerates all entries. Invalid ones are dropped from the database // after a warning log message, as a side-effect. -func (db *Lowlevel) PendingDevices() (map[protocol.DeviceID]ObservedDevice, error) { - iter, err := db.NewPrefixIterator([]byte{KeyTypePendingDevice}) - if err != nil { - return nil, err - } - defer iter.Release() +func (db *ObservedDB) PendingDevices() (map[protocol.DeviceID]ObservedDevice, error) { res := make(map[protocol.DeviceID]ObservedDevice) - for iter.Next() { - keyDev := db.keyer.DeviceFromPendingDeviceKey(iter.Key()) - deviceID, err := protocol.DeviceIDFromBytes(keyDev) + it, errFn := db.kv.PrefixKV("device/") + for kv := range it { + _, keyDev, ok := strings.Cut(kv.Key, "/") + if !ok { + if err := db.kv.DeleteKV(kv.Key); err != nil { + return nil, fmt.Errorf("delete invalid pending device: %w", err) + } + continue + } + + deviceID, err := protocol.DeviceIDFromString(keyDev) var protoD dbproto.ObservedDevice var od ObservedDevice if err != nil { goto deleteKey } - if err = proto.Unmarshal(iter.Value(), &protoD); err != nil { + if err = proto.Unmarshal(kv.Value, &protoD); err != nil { goto deleteKey } od.fromWire(&protoD) @@ -94,52 +106,37 @@ func (db *Lowlevel) PendingDevices() (map[protocol.DeviceID]ObservedDevice, erro // Deleting invalid entries is the only possible "repair" measure and // appropriate for the importance of pending entries. They will come back // soon if still relevant. - l.Infof("Invalid pending device entry, deleting from database: %x", iter.Key()) - if err := db.Delete(iter.Key()); err != nil { - return nil, err + if err := db.kv.DeleteKV(kv.Key); err != nil { + return nil, fmt.Errorf("delete invalid pending device: %w", err) } } - return res, nil + return res, errFn() } -func (db *Lowlevel) AddOrUpdatePendingFolder(id string, of ObservedFolder, device protocol.DeviceID) error { - key, err := db.keyer.GeneratePendingFolderKey(nil, device[:], []byte(id)) - if err != nil { - return err - } - return db.Put(key, mustMarshal(of.toWire())) +func (db *ObservedDB) AddOrUpdatePendingFolder(id string, of ObservedFolder, device protocol.DeviceID) error { + key := "folder/" + device.String() + "/" + id + return db.kv.PutKV(key, mustMarshal(of.toWire())) } // RemovePendingFolderForDevice removes entries for specific folder / device combinations. -func (db *Lowlevel) RemovePendingFolderForDevice(id string, device protocol.DeviceID) error { - key, err := db.keyer.GeneratePendingFolderKey(nil, device[:], []byte(id)) - if err != nil { - return err - } - return db.Delete(key) +func (db *ObservedDB) RemovePendingFolderForDevice(id string, device protocol.DeviceID) error { + key := "folder/" + device.String() + "/" + id + return db.kv.DeleteKV(key) } // RemovePendingFolder removes all entries matching a specific folder ID. -func (db *Lowlevel) RemovePendingFolder(id string) error { - iter, err := db.NewPrefixIterator([]byte{KeyTypePendingFolder}) - if err != nil { - return fmt.Errorf("creating iterator: %w", err) - } - defer iter.Release() - var iterErr error - for iter.Next() { - if id != string(db.keyer.FolderFromPendingFolderKey(iter.Key())) { +func (db *ObservedDB) RemovePendingFolder(id string) error { + it, errFn := db.kv.PrefixKV("folder/") + for kv := range it { + parts := strings.Split(kv.Key, "/") + if len(parts) != 3 || parts[2] != id { continue } - if err = db.Delete(iter.Key()); err != nil { - if iterErr != nil { - l.Debugf("Repeat error removing pending folder: %v", err) - } else { - iterErr = err - } + if err := db.kv.DeleteKV(kv.Key); err != nil { + return fmt.Errorf("delete pending folder: %w", err) } } - return iterErr + return errFn() } // Consolidated information about a pending folder @@ -147,41 +144,37 @@ type PendingFolder struct { OfferedBy map[protocol.DeviceID]ObservedFolder `json:"offeredBy"` } -func (db *Lowlevel) PendingFolders() (map[string]PendingFolder, error) { +func (db *ObservedDB) PendingFolders() (map[string]PendingFolder, error) { return db.PendingFoldersForDevice(protocol.EmptyDeviceID) } // PendingFoldersForDevice enumerates only entries matching the given device ID, unless it // is EmptyDeviceID. Invalid ones are dropped from the database after a info log // message, as a side-effect. -func (db *Lowlevel) PendingFoldersForDevice(device protocol.DeviceID) (map[string]PendingFolder, error) { - var err error - prefixKey := []byte{KeyTypePendingFolder} +func (db *ObservedDB) PendingFoldersForDevice(device protocol.DeviceID) (map[string]PendingFolder, error) { + prefix := "folder/" if device != protocol.EmptyDeviceID { - prefixKey, err = db.keyer.GeneratePendingFolderKey(nil, device[:], nil) - if err != nil { - return nil, err - } + prefix += device.String() + "/" } - iter, err := db.NewPrefixIterator(prefixKey) - if err != nil { - return nil, err - } - defer iter.Release() res := make(map[string]PendingFolder) - for iter.Next() { - keyDev, ok := db.keyer.DeviceFromPendingFolderKey(iter.Key()) - deviceID, err := protocol.DeviceIDFromBytes(keyDev) + it, errFn := db.kv.PrefixKV(prefix) + for kv := range it { + parts := strings.Split(kv.Key, "/") + if len(parts) != 3 { + continue + } + keyDev := parts[1] + deviceID, err := protocol.DeviceIDFromString(keyDev) var protoF dbproto.ObservedFolder var of ObservedFolder var folderID string - if !ok || err != nil { + if err != nil { goto deleteKey } - if folderID = string(db.keyer.FolderFromPendingFolderKey(iter.Key())); len(folderID) < 1 { + if folderID = parts[2]; len(folderID) < 1 { goto deleteKey } - if err = proto.Unmarshal(iter.Value(), &protoF); err != nil { + if err = proto.Unmarshal(kv.Value, &protoF); err != nil { goto deleteKey } if _, ok := res[folderID]; !ok { @@ -196,10 +189,17 @@ func (db *Lowlevel) PendingFoldersForDevice(device protocol.DeviceID) (map[strin // Deleting invalid entries is the only possible "repair" measure and // appropriate for the importance of pending entries. They will come back // soon if still relevant. - l.Infof("Invalid pending folder entry, deleting from database: %x", iter.Key()) - if err := db.Delete(iter.Key()); err != nil { - return nil, err + if err := db.kv.DeleteKV(kv.Key); err != nil { + return nil, fmt.Errorf("delete invalid pending folder: %w", err) } } - return res, nil + return res, errFn() +} + +func mustMarshal(m proto.Message) []byte { + bs, err := proto.Marshal(m) + if err != nil { + panic(err) + } + return bs } diff --git a/lib/db/backend/backend.go b/internal/db/olddb/backend/backend.go similarity index 93% rename from lib/db/backend/backend.go rename to internal/db/olddb/backend/backend.go index 0ab1795eb..789684d91 100644 --- a/lib/db/backend/backend.go +++ b/internal/db/olddb/backend/backend.go @@ -108,29 +108,8 @@ type Iterator interface { // is empty for a db in memory. type Backend interface { Reader - Writer NewReadTransaction() (ReadTransaction, error) - NewWriteTransaction(hooks ...CommitHook) (WriteTransaction, error) Close() error - Compact() error - Location() string -} - -type Tuning int - -const ( - // N.b. these constants must match those in lib/config.Tuning! - TuningAuto Tuning = iota - TuningSmall - TuningLarge -) - -func Open(path string, tuning Tuning) (Backend, error) { - return OpenLevelDB(path, tuning) -} - -func OpenMemory() Backend { - return OpenLevelDBMemory() } var ( diff --git a/internal/db/olddb/backend/leveldb_backend.go b/internal/db/olddb/backend/leveldb_backend.go new file mode 100644 index 000000000..c44eb285c --- /dev/null +++ b/internal/db/olddb/backend/leveldb_backend.go @@ -0,0 +1,113 @@ +// Copyright (C) 2018 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package backend + +import ( + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/iterator" + "github.com/syndtr/goleveldb/leveldb/util" +) + +// leveldbBackend implements Backend on top of a leveldb +type leveldbBackend struct { + ldb *leveldb.DB + closeWG *closeWaitGroup + location string +} + +func newLeveldbBackend(ldb *leveldb.DB, location string) *leveldbBackend { + return &leveldbBackend{ + ldb: ldb, + closeWG: &closeWaitGroup{}, + location: location, + } +} + +func (b *leveldbBackend) NewReadTransaction() (ReadTransaction, error) { + return b.newSnapshot() +} + +func (b *leveldbBackend) newSnapshot() (leveldbSnapshot, error) { + rel, err := newReleaser(b.closeWG) + if err != nil { + return leveldbSnapshot{}, err + } + snap, err := b.ldb.GetSnapshot() + if err != nil { + rel.Release() + return leveldbSnapshot{}, wrapLeveldbErr(err) + } + return leveldbSnapshot{ + snap: snap, + rel: rel, + }, nil +} + +func (b *leveldbBackend) Close() error { + b.closeWG.CloseWait() + return wrapLeveldbErr(b.ldb.Close()) +} + +func (b *leveldbBackend) Get(key []byte) ([]byte, error) { + val, err := b.ldb.Get(key, nil) + return val, wrapLeveldbErr(err) +} + +func (b *leveldbBackend) NewPrefixIterator(prefix []byte) (Iterator, error) { + return &leveldbIterator{b.ldb.NewIterator(util.BytesPrefix(prefix), nil)}, nil +} + +func (b *leveldbBackend) NewRangeIterator(first, last []byte) (Iterator, error) { + return &leveldbIterator{b.ldb.NewIterator(&util.Range{Start: first, Limit: last}, nil)}, nil +} + +func (b *leveldbBackend) Location() string { + return b.location +} + +// leveldbSnapshot implements backend.ReadTransaction +type leveldbSnapshot struct { + snap *leveldb.Snapshot + rel *releaser +} + +func (l leveldbSnapshot) Get(key []byte) ([]byte, error) { + val, err := l.snap.Get(key, nil) + return val, wrapLeveldbErr(err) +} + +func (l leveldbSnapshot) NewPrefixIterator(prefix []byte) (Iterator, error) { + return l.snap.NewIterator(util.BytesPrefix(prefix), nil), nil +} + +func (l leveldbSnapshot) NewRangeIterator(first, last []byte) (Iterator, error) { + return l.snap.NewIterator(&util.Range{Start: first, Limit: last}, nil), nil +} + +func (l leveldbSnapshot) Release() { + l.snap.Release() + l.rel.Release() +} + +type leveldbIterator struct { + iterator.Iterator +} + +func (it *leveldbIterator) Error() error { + return wrapLeveldbErr(it.Iterator.Error()) +} + +// wrapLeveldbErr wraps errors so that the backend package can recognize them +func wrapLeveldbErr(err error) error { + switch err { + case leveldb.ErrClosed: + return errClosed + case leveldb.ErrNotFound: + return errNotFound + } + return err +} diff --git a/internal/db/olddb/backend/leveldb_open.go b/internal/db/olddb/backend/leveldb_open.go new file mode 100644 index 000000000..344426ae0 --- /dev/null +++ b/internal/db/olddb/backend/leveldb_open.go @@ -0,0 +1,32 @@ +// Copyright (C) 2018 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package backend + +import ( + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/opt" +) + +const dbMaxOpenFiles = 100 + +// OpenLevelDBRO attempts to open the database at the given location, read +// only. +func OpenLevelDBRO(location string) (Backend, error) { + opts := &opt.Options{ + OpenFilesCacheCapacity: dbMaxOpenFiles, + ReadOnly: true, + } + ldb, err := open(location, opts) + if err != nil { + return nil, err + } + return newLeveldbBackend(ldb, location), nil +} + +func open(location string, opts *opt.Options) (*leveldb.DB, error) { + return leveldb.OpenFile(location, opts) +} diff --git a/lib/db/keyer.go b/internal/db/olddb/keyer.go similarity index 99% rename from lib/db/keyer.go rename to internal/db/olddb/keyer.go index 137b5cc29..3c335fd52 100644 --- a/lib/db/keyer.go +++ b/internal/db/olddb/keyer.go @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at https://mozilla.org/MPL/2.0/. -package db +package olddb import ( "encoding/binary" diff --git a/internal/db/olddb/lowlevel.go b/internal/db/olddb/lowlevel.go new file mode 100644 index 000000000..3afad4c76 --- /dev/null +++ b/internal/db/olddb/lowlevel.go @@ -0,0 +1,70 @@ +// Copyright (C) 2014 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package olddb + +import ( + "encoding/binary" + "time" + + "github.com/syncthing/syncthing/internal/db/olddb/backend" +) + +// deprecatedLowlevel is the lowest level database interface. It has a very simple +// purpose: hold the actual backend database, and the in-memory state +// that belong to that database. In the same way that a single on disk +// database can only be opened once, there should be only one deprecatedLowlevel for +// any given backend. +type deprecatedLowlevel struct { + backend.Backend + folderIdx *smallIndex + deviceIdx *smallIndex + keyer keyer +} + +func NewLowlevel(backend backend.Backend) (*deprecatedLowlevel, error) { + // Only log restarts in debug mode. + db := &deprecatedLowlevel{ + Backend: backend, + folderIdx: newSmallIndex(backend, []byte{KeyTypeFolderIdx}), + deviceIdx: newSmallIndex(backend, []byte{KeyTypeDeviceIdx}), + } + db.keyer = newDefaultKeyer(db.folderIdx, db.deviceIdx) + return db, nil +} + +// ListFolders returns the list of folders currently in the database +func (db *deprecatedLowlevel) ListFolders() []string { + return db.folderIdx.Values() +} + +func (db *deprecatedLowlevel) IterateMtimes(fn func(folder, name string, ondisk, virtual time.Time) error) error { + it, err := db.NewPrefixIterator([]byte{KeyTypeVirtualMtime}) + if err != nil { + return err + } + defer it.Release() + for it.Next() { + key := it.Key()[1:] + folderID, ok := db.folderIdx.Val(binary.BigEndian.Uint32(key)) + if !ok { + continue + } + name := key[4:] + val := it.Value() + var ondisk, virtual time.Time + if err := ondisk.UnmarshalBinary(val[:len(val)/2]); err != nil { + continue + } + if err := virtual.UnmarshalBinary(val[len(val)/2:]); err != nil { + continue + } + if err := fn(string(folderID), string(name), ondisk, virtual); err != nil { + return err + } + } + return it.Error() +} diff --git a/internal/db/olddb/set.go b/internal/db/olddb/set.go new file mode 100644 index 000000000..be16e23cc --- /dev/null +++ b/internal/db/olddb/set.go @@ -0,0 +1,67 @@ +// Copyright (C) 2014 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +// Package db provides a set type to track local/remote files with newness +// checks. We must do a certain amount of normalization in here. We will get +// fed paths with either native or wire-format separators and encodings +// depending on who calls us. We transform paths to wire-format (NFC and +// slashes) on the way to the database, and transform to native format +// (varying separator and encoding) on the way back out. +package olddb + +import ( + "github.com/syncthing/syncthing/lib/osutil" + "github.com/syncthing/syncthing/lib/protocol" +) + +type deprecatedFileSet struct { + folder string + db *deprecatedLowlevel +} + +// The Iterator is called with either a protocol.FileInfo or a +// FileInfoTruncated (depending on the method) and returns true to +// continue iteration, false to stop. +type Iterator func(f protocol.FileInfo) bool + +func NewFileSet(folder string, db *deprecatedLowlevel) (*deprecatedFileSet, error) { + s := &deprecatedFileSet{ + folder: folder, + db: db, + } + return s, nil +} + +type Snapshot struct { + folder string + t readOnlyTransaction +} + +func (s *deprecatedFileSet) Snapshot() (*Snapshot, error) { + t, err := s.db.newReadOnlyTransaction() + if err != nil { + return nil, err + } + return &Snapshot{ + folder: s.folder, + t: t, + }, nil +} + +func (s *Snapshot) Release() { + s.t.close() +} + +func (s *Snapshot) WithHaveSequence(startSeq int64, fn Iterator) error { + return s.t.withHaveSequence([]byte(s.folder), startSeq, nativeFileIterator(fn)) +} + +func nativeFileIterator(fn Iterator) Iterator { + return func(fi protocol.FileInfo) bool { + fi.Name = osutil.NativeFilename(fi.Name) + return fn(fi) + } +} diff --git a/lib/db/smallindex.go b/internal/db/olddb/smallindex.go similarity index 71% rename from lib/db/smallindex.go rename to internal/db/olddb/smallindex.go index c53c69804..6950f8f2f 100644 --- a/lib/db/smallindex.go +++ b/internal/db/olddb/smallindex.go @@ -4,13 +4,13 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at https://mozilla.org/MPL/2.0/. -package db +package olddb import ( "encoding/binary" "slices" - "github.com/syncthing/syncthing/lib/db/backend" + "github.com/syncthing/syncthing/internal/db/olddb/backend" "github.com/syncthing/syncthing/lib/sync" ) @@ -74,23 +74,7 @@ func (i *smallIndex) ID(val []byte) (uint32, error) { return id, nil } - id := i.nextID - i.nextID++ - - valStr := string(val) - i.val2id[valStr] = id - i.id2val[id] = valStr - - key := make([]byte, len(i.prefix)+8) // prefix plus uint32 id - copy(key, i.prefix) - binary.BigEndian.PutUint32(key[len(i.prefix):], id) - if err := i.db.Put(key, val); err != nil { - i.mut.Unlock() - return 0, err - } - - i.mut.Unlock() - return id, nil + panic("missing ID") } // Val returns the value for the given index number, or (nil, false) if there @@ -106,33 +90,6 @@ func (i *smallIndex) Val(id uint32) ([]byte, bool) { return []byte(val), true } -func (i *smallIndex) Delete(val []byte) error { - i.mut.Lock() - defer i.mut.Unlock() - - // Check the reverse mapping to get the ID for the value. - if id, ok := i.val2id[string(val)]; ok { - // Generate the corresponding database key. - key := make([]byte, len(i.prefix)+8) // prefix plus uint32 id - copy(key, i.prefix) - binary.BigEndian.PutUint32(key[len(i.prefix):], id) - - // Put an empty value into the database. This indicates that the - // entry does not exist any more and prevents the ID from being - // reused in the future. - if err := i.db.Put(key, []byte{}); err != nil { - return err - } - - // Delete reverse mapping. - delete(i.id2val, id) - } - - // Delete forward mapping. - delete(i.val2id, string(val)) - return nil -} - // Values returns the set of values in the index func (i *smallIndex) Values() []string { // In principle this method should return [][]byte because all the other diff --git a/internal/db/olddb/transactions.go b/internal/db/olddb/transactions.go new file mode 100644 index 000000000..6f6006b06 --- /dev/null +++ b/internal/db/olddb/transactions.go @@ -0,0 +1,193 @@ +// Copyright (C) 2014 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package olddb + +import ( + "fmt" + + "google.golang.org/protobuf/proto" + + "github.com/syncthing/syncthing/internal/db/olddb/backend" + "github.com/syncthing/syncthing/internal/gen/bep" + "github.com/syncthing/syncthing/internal/gen/dbproto" + "github.com/syncthing/syncthing/lib/protocol" +) + +// A readOnlyTransaction represents a database snapshot. +type readOnlyTransaction struct { + backend.ReadTransaction + keyer keyer +} + +func (db *deprecatedLowlevel) newReadOnlyTransaction() (readOnlyTransaction, error) { + tran, err := db.NewReadTransaction() + if err != nil { + return readOnlyTransaction{}, err + } + return db.readOnlyTransactionFromBackendTransaction(tran), nil +} + +func (db *deprecatedLowlevel) readOnlyTransactionFromBackendTransaction(tran backend.ReadTransaction) readOnlyTransaction { + return readOnlyTransaction{ + ReadTransaction: tran, + keyer: db.keyer, + } +} + +func (t readOnlyTransaction) close() { + t.Release() +} + +func (t readOnlyTransaction) getFileByKey(key []byte) (protocol.FileInfo, bool, error) { + f, ok, err := t.getFileTrunc(key, false) + if err != nil || !ok { + return protocol.FileInfo{}, false, err + } + return f, true, nil +} + +func (t readOnlyTransaction) getFileTrunc(key []byte, trunc bool) (protocol.FileInfo, bool, error) { + bs, err := t.Get(key) + if backend.IsNotFound(err) { + return protocol.FileInfo{}, false, nil + } + if err != nil { + return protocol.FileInfo{}, false, err + } + f, err := t.unmarshalTrunc(bs, trunc) + if backend.IsNotFound(err) { + return protocol.FileInfo{}, false, nil + } + if err != nil { + return protocol.FileInfo{}, false, err + } + return f, true, nil +} + +func (t readOnlyTransaction) unmarshalTrunc(bs []byte, trunc bool) (protocol.FileInfo, error) { + if trunc { + var bfi dbproto.FileInfoTruncated + err := proto.Unmarshal(bs, &bfi) + if err != nil { + return protocol.FileInfo{}, err + } + if err := t.fillTruncated(&bfi); err != nil { + return protocol.FileInfo{}, err + } + return protocol.FileInfoFromDBTruncated(&bfi), nil + } + + var bfi bep.FileInfo + err := proto.Unmarshal(bs, &bfi) + if err != nil { + return protocol.FileInfo{}, err + } + if err := t.fillFileInfo(&bfi); err != nil { + return protocol.FileInfo{}, err + } + return protocol.FileInfoFromDB(&bfi), nil +} + +type blocksIndirectionError struct { + err error +} + +func (e *blocksIndirectionError) Error() string { + return fmt.Sprintf("filling Blocks: %v", e.err) +} + +func (e *blocksIndirectionError) Unwrap() error { + return e.err +} + +// fillFileInfo follows the (possible) indirection of blocks and version +// vector and fills it out. +func (t readOnlyTransaction) fillFileInfo(fi *bep.FileInfo) error { + var key []byte + + if len(fi.Blocks) == 0 && len(fi.BlocksHash) != 0 { + // The blocks list is indirected and we need to load it. + key = t.keyer.GenerateBlockListKey(key, fi.BlocksHash) + bs, err := t.Get(key) + if err != nil { + return &blocksIndirectionError{err} + } + var bl dbproto.BlockList + if err := proto.Unmarshal(bs, &bl); err != nil { + return err + } + fi.Blocks = bl.Blocks + } + + if len(fi.VersionHash) != 0 { + key = t.keyer.GenerateVersionKey(key, fi.VersionHash) + bs, err := t.Get(key) + if err != nil { + return fmt.Errorf("filling Version: %w", err) + } + var v bep.Vector + if err := proto.Unmarshal(bs, &v); err != nil { + return err + } + fi.Version = &v + } + + return nil +} + +// fillTruncated follows the (possible) indirection of version vector and +// fills it. +func (t readOnlyTransaction) fillTruncated(fi *dbproto.FileInfoTruncated) error { + var key []byte + + if len(fi.VersionHash) == 0 { + return nil + } + + key = t.keyer.GenerateVersionKey(key, fi.VersionHash) + bs, err := t.Get(key) + if err != nil { + return err + } + var v bep.Vector + if err := proto.Unmarshal(bs, &v); err != nil { + return err + } + fi.Version = &v + return nil +} + +func (t *readOnlyTransaction) withHaveSequence(folder []byte, startSeq int64, fn Iterator) error { + first, err := t.keyer.GenerateSequenceKey(nil, folder, startSeq) + if err != nil { + return err + } + last, err := t.keyer.GenerateSequenceKey(nil, folder, maxInt64) + if err != nil { + return err + } + dbi, err := t.NewRangeIterator(first, last) + if err != nil { + return err + } + defer dbi.Release() + + for dbi.Next() { + f, ok, err := t.getFileByKey(dbi.Value()) + if err != nil { + return err + } + if !ok { + continue + } + + if !fn(f) { + return nil + } + } + return dbi.Error() +} diff --git a/internal/db/sqlite/basedb.go b/internal/db/sqlite/basedb.go new file mode 100644 index 000000000..c2d1b8808 --- /dev/null +++ b/internal/db/sqlite/basedb.go @@ -0,0 +1,249 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package sqlite + +import ( + "database/sql" + "embed" + "io/fs" + "path/filepath" + "strconv" + "strings" + "sync" + "text/template" + "time" + + "github.com/jmoiron/sqlx" + "github.com/syncthing/syncthing/lib/build" + "github.com/syncthing/syncthing/lib/protocol" +) + +const currentSchemaVersion = 1 + +//go:embed sql/** +var embedded embed.FS + +type baseDB struct { + path string + baseName string + sql *sqlx.DB + + updateLock sync.Mutex + updatePoints int + checkpointsCount int + + statementsMut sync.RWMutex + statements map[string]*sqlx.Stmt + tplInput map[string]any +} + +func openBase(path string, maxConns int, pragmas, schemaScripts, migrationScripts []string) (*baseDB, error) { + // Open the database with options to enable foreign keys and recursive + // triggers (needed for the delete+insert triggers on row replace). + sqlDB, err := sqlx.Open(dbDriver, "file:"+path+"?"+commonOptions) + if err != nil { + return nil, wrap(err) + } + + sqlDB.SetMaxOpenConns(maxConns) + + for _, pragma := range pragmas { + if _, err := sqlDB.Exec("PRAGMA " + pragma); err != nil { + return nil, wrap(err, "PRAGMA "+pragma) + } + } + + db := &baseDB{ + path: path, + baseName: filepath.Base(path), + sql: sqlDB, + statements: make(map[string]*sqlx.Stmt), + } + + for _, script := range schemaScripts { + if err := db.runScripts(script); err != nil { + return nil, wrap(err) + } + } + + ver, _ := db.getAppliedSchemaVersion() + if ver.SchemaVersion > 0 { + filter := func(scr string) bool { + scr = filepath.Base(scr) + nstr, _, ok := strings.Cut(scr, "-") + if !ok { + return false + } + n, err := strconv.ParseInt(nstr, 10, 32) + if err != nil { + return false + } + return int(n) > ver.SchemaVersion + } + for _, script := range migrationScripts { + if err := db.runScripts(script, filter); err != nil { + return nil, wrap(err) + } + } + } + + // Set the current schema version, if not already set + if err := db.setAppliedSchemaVersion(currentSchemaVersion); err != nil { + return nil, wrap(err) + } + + db.tplInput = map[string]any{ + "FlagLocalUnsupported": protocol.FlagLocalUnsupported, + "FlagLocalIgnored": protocol.FlagLocalIgnored, + "FlagLocalMustRescan": protocol.FlagLocalMustRescan, + "FlagLocalReceiveOnly": protocol.FlagLocalReceiveOnly, + "FlagLocalGlobal": protocol.FlagLocalGlobal, + "FlagLocalNeeded": protocol.FlagLocalNeeded, + "SyncthingVersion": build.LongVersion, + } + + return db, nil +} + +func (s *baseDB) Close() error { + s.updateLock.Lock() + s.statementsMut.Lock() + defer s.updateLock.Unlock() + defer s.statementsMut.Unlock() + for _, stmt := range s.statements { + stmt.Close() + } + return wrap(s.sql.Close()) +} + +var tplFuncs = template.FuncMap{ + "or": func(vs ...int) int { + v := vs[0] + for _, ov := range vs[1:] { + v |= ov + } + return v + }, +} + +// stmt returns a prepared statement for the given SQL string, after +// applying local template expansions. The statement is cached. +func (s *baseDB) stmt(tpl string) stmt { + tpl = strings.TrimSpace(tpl) + + // Fast concurrent lookup of cached statement + s.statementsMut.RLock() + stmt, ok := s.statements[tpl] + s.statementsMut.RUnlock() + if ok { + return stmt + } + + // On miss, take the full lock, check again + s.statementsMut.Lock() + defer s.statementsMut.Unlock() + stmt, ok = s.statements[tpl] + if ok { + return stmt + } + + // Apply template expansions + var sb strings.Builder + compTpl := template.Must(template.New("tpl").Funcs(tplFuncs).Parse(tpl)) + if err := compTpl.Execute(&sb, s.tplInput); err != nil { + panic("bug: bad template: " + err.Error()) + } + + // Prepare and cache + stmt, err := s.sql.Preparex(sb.String()) + if err != nil { + return failedStmt{err} + } + s.statements[tpl] = stmt + return stmt +} + +type stmt interface { + Exec(args ...any) (sql.Result, error) + Get(dest any, args ...any) error + Queryx(args ...any) (*sqlx.Rows, error) + Select(dest any, args ...any) error +} + +type failedStmt struct { + err error +} + +func (f failedStmt) Exec(_ ...any) (sql.Result, error) { return nil, f.err } +func (f failedStmt) Get(_ any, _ ...any) error { return f.err } +func (f failedStmt) Queryx(_ ...any) (*sqlx.Rows, error) { return nil, f.err } +func (f failedStmt) Select(_ any, _ ...any) error { return f.err } + +func (s *baseDB) runScripts(glob string, filter ...func(s string) bool) error { + scripts, err := fs.Glob(embedded, glob) + if err != nil { + return wrap(err) + } + + tx, err := s.sql.Begin() + if err != nil { + return wrap(err) + } + defer tx.Rollback() //nolint:errcheck + +nextScript: + for _, scr := range scripts { + for _, fn := range filter { + if !fn(scr) { + continue nextScript + } + } + bs, err := fs.ReadFile(embedded, scr) + if err != nil { + return wrap(err, scr) + } + // SQLite requires one statement per exec, so we split the init + // files on lines containing only a semicolon and execute them + // separately. We require it on a separate line because there are + // also statement-internal semicolons in the triggers. + for _, stmt := range strings.Split(string(bs), "\n;") { + if _, err := tx.Exec(stmt); err != nil { + return wrap(err, stmt) + } + } + } + + return wrap(tx.Commit()) +} + +type schemaVersion struct { + SchemaVersion int + AppliedAt int64 + SyncthingVersion string +} + +func (s *schemaVersion) AppliedTime() time.Time { + return time.Unix(0, s.AppliedAt) +} + +func (s *baseDB) setAppliedSchemaVersion(ver int) error { + _, err := s.stmt(` + INSERT OR IGNORE INTO schemamigrations (schema_version, applied_at, syncthing_version) + VALUES (?, ?, ?) + `).Exec(ver, time.Now().UnixNano(), build.LongVersion) + return wrap(err) +} + +func (s *baseDB) getAppliedSchemaVersion() (schemaVersion, error) { + var v schemaVersion + err := s.stmt(` + SELECT schema_version as schemaversion, applied_at as appliedat, syncthing_version as syncthingversion FROM schemamigrations + ORDER BY schema_version DESC + LIMIT 1 + `).Get(&v) + return v, wrap(err) +} diff --git a/internal/db/sqlite/db_bench_test.go b/internal/db/sqlite/db_bench_test.go new file mode 100644 index 000000000..5e0f79574 --- /dev/null +++ b/internal/db/sqlite/db_bench_test.go @@ -0,0 +1,243 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package sqlite + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/syncthing/syncthing/internal/timeutil" + "github.com/syncthing/syncthing/lib/config" + "github.com/syncthing/syncthing/lib/protocol" + "github.com/syncthing/syncthing/lib/rand" +) + +var globalFi protocol.FileInfo + +func BenchmarkUpdate(b *testing.B) { + db, err := OpenTemp() + if err != nil { + b.Fatal(err) + } + b.Cleanup(func() { + if err := db.Close(); err != nil { + b.Fatal(err) + } + }) + svc := db.Service(time.Hour).(*Service) + + fs := make([]protocol.FileInfo, 100) + seed := 0 + + size := 10000 + for size < 200_000 { + t0 := time.Now() + if err := svc.periodic(context.Background()); err != nil { + b.Fatal(err) + } + b.Log("garbage collect in", time.Since(t0)) + + for { + local, err := db.CountLocal(folderID, protocol.LocalDeviceID) + if err != nil { + b.Fatal(err) + } + if local.Files >= size { + break + } + fs := make([]protocol.FileInfo, 1000) + for i := range fs { + fs[i] = genFile(rand.String(24), 64, 0) + } + if err := db.Update(folderID, protocol.LocalDeviceID, fs); err != nil { + b.Fatal(err) + } + } + + b.Run(fmt.Sprintf("Insert100Loc@%d", size), func(b *testing.B) { + for range b.N { + for i := range fs { + fs[i] = genFile(rand.String(24), 64, 0) + } + if err := db.Update(folderID, protocol.LocalDeviceID, fs); err != nil { + b.Fatal(err) + } + } + b.ReportMetric(float64(b.N)*100.0/b.Elapsed().Seconds(), "files/s") + }) + + b.Run(fmt.Sprintf("RepBlocks100@%d", size), func(b *testing.B) { + for range b.N { + for i := range fs { + fs[i].Blocks = genBlocks(fs[i].Name, seed, 64) + fs[i].Version = fs[i].Version.Update(42) + } + seed++ + if err := db.Update(folderID, protocol.LocalDeviceID, fs); err != nil { + b.Fatal(err) + } + } + b.ReportMetric(float64(b.N)*100.0/b.Elapsed().Seconds(), "files/s") + }) + + b.Run(fmt.Sprintf("RepSame100@%d", size), func(b *testing.B) { + for range b.N { + for i := range fs { + fs[i].Version = fs[i].Version.Update(42) + } + if err := db.Update(folderID, protocol.LocalDeviceID, fs); err != nil { + b.Fatal(err) + } + } + b.ReportMetric(float64(b.N)*100.0/b.Elapsed().Seconds(), "files/s") + }) + + b.Run(fmt.Sprintf("Insert100Rem@%d", size), func(b *testing.B) { + for range b.N { + for i := range fs { + fs[i].Blocks = genBlocks(fs[i].Name, seed, 64) + fs[i].Version = fs[i].Version.Update(42) + fs[i].Sequence = timeutil.StrictlyMonotonicNanos() + } + if err := db.Update(folderID, protocol.DeviceID{42}, fs); err != nil { + b.Fatal(err) + } + } + b.ReportMetric(float64(b.N)*100.0/b.Elapsed().Seconds(), "files/s") + }) + + b.Run(fmt.Sprintf("GetGlobal100@%d", size), func(b *testing.B) { + for range b.N { + for i := range fs { + _, ok, err := db.GetGlobalFile(folderID, fs[i].Name) + if err != nil { + b.Fatal(err) + } + if !ok { + b.Fatal("should exist") + } + } + } + b.ReportMetric(float64(b.N)*100.0/b.Elapsed().Seconds(), "files/s") + }) + + b.Run(fmt.Sprintf("LocalSequenced@%d", size), func(b *testing.B) { + count := 0 + for range b.N { + cur, err := db.GetDeviceSequence(folderID, protocol.LocalDeviceID) + if err != nil { + b.Fatal(err) + } + it, errFn := db.AllLocalFilesBySequence(folderID, protocol.LocalDeviceID, cur-100, 0) + for f := range it { + count++ + globalFi = f + } + if err := errFn(); err != nil { + b.Fatal(err) + } + } + b.ReportMetric(float64(count)/b.Elapsed().Seconds(), "files/s") + }) + + b.Run(fmt.Sprintf("GetDeviceSequenceLoc@%d", size), func(b *testing.B) { + for range b.N { + _, err := db.GetDeviceSequence(folderID, protocol.LocalDeviceID) + if err != nil { + b.Fatal(err) + } + } + }) + b.Run(fmt.Sprintf("GetDeviceSequenceRem@%d", size), func(b *testing.B) { + for range b.N { + _, err := db.GetDeviceSequence(folderID, protocol.DeviceID{42}) + if err != nil { + b.Fatal(err) + } + } + }) + + b.Run(fmt.Sprintf("RemoteNeed@%d", size), func(b *testing.B) { + count := 0 + for range b.N { + it, errFn := db.AllNeededGlobalFiles(folderID, protocol.DeviceID{42}, config.PullOrderAlphabetic, 0, 0) + for f := range it { + count++ + globalFi = f + } + if err := errFn(); err != nil { + b.Fatal(err) + } + } + b.ReportMetric(float64(count)/b.Elapsed().Seconds(), "files/s") + }) + + b.Run(fmt.Sprintf("LocalNeed100Largest@%d", size), func(b *testing.B) { + count := 0 + for range b.N { + it, errFn := db.AllNeededGlobalFiles(folderID, protocol.LocalDeviceID, config.PullOrderLargestFirst, 100, 0) + for f := range it { + globalFi = f + count++ + } + if err := errFn(); err != nil { + b.Fatal(err) + } + } + b.ReportMetric(float64(count)/b.Elapsed().Seconds(), "files/s") + }) + + size <<= 1 + } +} + +func TestBenchmarkDropAllRemote(t *testing.T) { + if testing.Short() { + t.Skip("slow test") + } + + db, err := OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := db.Close(); err != nil { + t.Fatal(err) + } + }) + + fs := make([]protocol.FileInfo, 1000) + seq := 0 + for { + local, err := db.CountLocal(folderID, protocol.LocalDeviceID) + if err != nil { + t.Fatal(err) + } + if local.Files >= 15_000 { + break + } + for i := range fs { + seq++ + fs[i] = genFile(rand.String(24), 64, seq) + } + if err := db.Update(folderID, protocol.DeviceID{42}, fs); err != nil { + t.Fatal(err) + } + if err := db.Update(folderID, protocol.LocalDeviceID, fs); err != nil { + t.Fatal(err) + } + } + + t0 := time.Now() + if err := db.DropAllFiles(folderID, protocol.DeviceID{42}); err != nil { + t.Fatal(err) + } + d := time.Since(t0) + t.Log("drop all took", d) +} diff --git a/internal/db/sqlite/db_folderdb.go b/internal/db/sqlite/db_folderdb.go new file mode 100644 index 000000000..077b73a6a --- /dev/null +++ b/internal/db/sqlite/db_folderdb.go @@ -0,0 +1,401 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package sqlite + +import ( + "database/sql" + "errors" + "fmt" + "iter" + "path/filepath" + "strings" + "time" + + "github.com/syncthing/syncthing/internal/db" + "github.com/syncthing/syncthing/lib/config" + "github.com/syncthing/syncthing/lib/protocol" + "github.com/syncthing/syncthing/lib/rand" +) + +var errNoSuchFolder = errors.New("no such folder") + +func (s *DB) getFolderDB(folder string, create bool) (*folderDB, error) { + // Check for an already open database + s.folderDBsMut.RLock() + fdb, ok := s.folderDBs[folder] + s.folderDBsMut.RUnlock() + if ok { + return fdb, nil + } + + // Check for an existing database. If we're not supposed to create the + // folder, we don't move on if it doesn't already have a database name. + var dbns sql.NullString + if err := s.stmt(` + SELECT database_name FROM folders + WHERE folder_id = ? + `).Get(&dbns, folder); err != nil && !errors.Is(err, sql.ErrNoRows) { + return nil, wrap(err) + } + + var dbName string + if dbns.Valid { + dbName = dbns.String + } + if dbName == "" && !create { + return nil, errNoSuchFolder + } + + // Create a folder ID and database if it does not already exist + s.folderDBsMut.Lock() + defer s.folderDBsMut.Unlock() + if fdb, ok := s.folderDBs[folder]; ok { + return fdb, nil + } + + if dbName == "" { + // First time we want to access this folder, need to create a new + // folder ID + + s.updateLock.Lock() + defer s.updateLock.Unlock() + + idx, err := s.folderIdxLocked(folder) + if err != nil { + return nil, wrap(err) + } + + // The database name is the folder index ID and a random slug. + slug := strings.ToLower(rand.String(8)) + dbName = fmt.Sprintf("folder.%04x-%s.db", idx, slug) + if _, err := s.stmt(`UPDATE folders SET database_name = ? WHERE idx = ?`).Exec(dbName, idx); err != nil { + return nil, wrap(err, "set name") + } + } + + l.Debugf("Folder %s in database %s", folder, dbName) + path := dbName + if !filepath.IsAbs(path) { + path = filepath.Join(s.pathBase, dbName) + } + fdb, err := s.folderDBOpener(folder, path, s.deleteRetention) + if err != nil { + return nil, wrap(err) + } + s.folderDBs[folder] = fdb + return fdb, nil +} + +func (s *DB) Update(folder string, device protocol.DeviceID, fs []protocol.FileInfo) error { + fdb, err := s.getFolderDB(folder, true) + if err != nil { + return err + } + return fdb.Update(device, fs) +} + +func (s *DB) GetDeviceFile(folder string, device protocol.DeviceID, file string) (protocol.FileInfo, bool, error) { + fdb, err := s.getFolderDB(folder, false) + if errors.Is(err, errNoSuchFolder) { + return protocol.FileInfo{}, false, nil + } + if err != nil { + return protocol.FileInfo{}, false, err + } + return fdb.GetDeviceFile(device, file) +} + +func (s *DB) GetGlobalAvailability(folder, file string) ([]protocol.DeviceID, error) { + fdb, err := s.getFolderDB(folder, false) + if errors.Is(err, errNoSuchFolder) { + return nil, nil + } + if err != nil { + return nil, err + } + return fdb.GetGlobalAvailability(file) +} + +func (s *DB) GetGlobalFile(folder string, file string) (protocol.FileInfo, bool, error) { + fdb, err := s.getFolderDB(folder, false) + if errors.Is(err, errNoSuchFolder) { + return protocol.FileInfo{}, false, nil + } + if err != nil { + return protocol.FileInfo{}, false, err + } + return fdb.GetGlobalFile(file) +} + +func (s *DB) AllGlobalFiles(folder string) (iter.Seq[db.FileMetadata], func() error) { + fdb, err := s.getFolderDB(folder, false) + if errors.Is(err, errNoSuchFolder) { + return func(yield func(db.FileMetadata) bool) {}, func() error { return nil } + } + if err != nil { + return func(yield func(db.FileMetadata) bool) {}, func() error { return err } + } + return fdb.AllGlobalFiles() +} + +func (s *DB) AllGlobalFilesPrefix(folder string, prefix string) (iter.Seq[db.FileMetadata], func() error) { + fdb, err := s.getFolderDB(folder, false) + if errors.Is(err, errNoSuchFolder) { + return func(yield func(db.FileMetadata) bool) {}, func() error { return nil } + } + if err != nil { + return func(yield func(db.FileMetadata) bool) {}, func() error { return err } + } + return fdb.AllGlobalFilesPrefix(prefix) +} + +func (s *DB) AllLocalBlocksWithHash(folder string, hash []byte) (iter.Seq[db.BlockMapEntry], func() error) { + fdb, err := s.getFolderDB(folder, false) + if errors.Is(err, errNoSuchFolder) { + return func(yield func(db.BlockMapEntry) bool) {}, func() error { return nil } + } + if err != nil { + return func(yield func(db.BlockMapEntry) bool) {}, func() error { return err } + } + return fdb.AllLocalBlocksWithHash(hash) +} + +func (s *DB) AllLocalFiles(folder string, device protocol.DeviceID) (iter.Seq[protocol.FileInfo], func() error) { + fdb, err := s.getFolderDB(folder, false) + if errors.Is(err, errNoSuchFolder) { + return func(yield func(protocol.FileInfo) bool) {}, func() error { return nil } + } + if err != nil { + return func(yield func(protocol.FileInfo) bool) {}, func() error { return err } + } + return fdb.AllLocalFiles(device) +} + +func (s *DB) AllLocalFilesBySequence(folder string, device protocol.DeviceID, startSeq int64, limit int) (iter.Seq[protocol.FileInfo], func() error) { + fdb, err := s.getFolderDB(folder, false) + if errors.Is(err, errNoSuchFolder) { + return func(yield func(protocol.FileInfo) bool) {}, func() error { return nil } + } + if err != nil { + return func(yield func(protocol.FileInfo) bool) {}, func() error { return err } + } + return fdb.AllLocalFilesBySequence(device, startSeq, limit) +} + +func (s *DB) AllLocalFilesWithPrefix(folder string, device protocol.DeviceID, prefix string) (iter.Seq[protocol.FileInfo], func() error) { + fdb, err := s.getFolderDB(folder, false) + if errors.Is(err, errNoSuchFolder) { + return func(yield func(protocol.FileInfo) bool) {}, func() error { return nil } + } + if err != nil { + return func(yield func(protocol.FileInfo) bool) {}, func() error { return err } + } + return fdb.AllLocalFilesWithPrefix(device, prefix) +} + +func (s *DB) AllLocalFilesWithBlocksHash(folder string, h []byte) (iter.Seq[db.FileMetadata], func() error) { + fdb, err := s.getFolderDB(folder, false) + if errors.Is(err, errNoSuchFolder) { + return func(yield func(db.FileMetadata) bool) {}, func() error { return nil } + } + if err != nil { + return func(yield func(db.FileMetadata) bool) {}, func() error { return err } + } + return fdb.AllLocalFilesWithBlocksHash(h) +} + +func (s *DB) AllNeededGlobalFiles(folder string, device protocol.DeviceID, order config.PullOrder, limit, offset int) (iter.Seq[protocol.FileInfo], func() error) { + fdb, err := s.getFolderDB(folder, false) + if errors.Is(err, errNoSuchFolder) { + return func(yield func(protocol.FileInfo) bool) {}, func() error { return nil } + } + if err != nil { + return func(yield func(protocol.FileInfo) bool) {}, func() error { return err } + } + return fdb.AllNeededGlobalFiles(device, order, limit, offset) +} + +func (s *DB) DropAllFiles(folder string, device protocol.DeviceID) error { + fdb, err := s.getFolderDB(folder, false) + if errors.Is(err, errNoSuchFolder) { + return nil + } + if err != nil { + return err + } + return fdb.DropAllFiles(device) +} + +func (s *DB) DropFilesNamed(folder string, device protocol.DeviceID, names []string) error { + fdb, err := s.getFolderDB(folder, false) + if errors.Is(err, errNoSuchFolder) { + return nil + } + if err != nil { + return err + } + return fdb.DropFilesNamed(device, names) +} + +func (s *DB) ListDevicesForFolder(folder string) ([]protocol.DeviceID, error) { + fdb, err := s.getFolderDB(folder, false) + if errors.Is(err, errNoSuchFolder) { + return nil, nil + } + if err != nil { + return nil, err + } + return fdb.ListDevicesForFolder() +} + +func (s *DB) RemoteSequences(folder string) (map[protocol.DeviceID]int64, error) { + fdb, err := s.getFolderDB(folder, false) + if errors.Is(err, errNoSuchFolder) { + return nil, nil //nolint:nilnil + } + if err != nil { + return nil, err + } + return fdb.RemoteSequences() +} + +func (s *DB) CountGlobal(folder string) (db.Counts, error) { + fdb, err := s.getFolderDB(folder, false) + if errors.Is(err, errNoSuchFolder) { + return db.Counts{}, nil + } + if err != nil { + return db.Counts{}, err + } + return fdb.CountGlobal() +} + +func (s *DB) CountLocal(folder string, device protocol.DeviceID) (db.Counts, error) { + fdb, err := s.getFolderDB(folder, false) + if errors.Is(err, errNoSuchFolder) { + return db.Counts{}, nil + } + if err != nil { + return db.Counts{}, err + } + return fdb.CountLocal(device) +} + +func (s *DB) CountNeed(folder string, device protocol.DeviceID) (db.Counts, error) { + fdb, err := s.getFolderDB(folder, false) + if errors.Is(err, errNoSuchFolder) { + return db.Counts{}, nil + } + if err != nil { + return db.Counts{}, err + } + return fdb.CountNeed(device) +} + +func (s *DB) CountReceiveOnlyChanged(folder string) (db.Counts, error) { + fdb, err := s.getFolderDB(folder, false) + if errors.Is(err, errNoSuchFolder) { + return db.Counts{}, nil + } + if err != nil { + return db.Counts{}, err + } + return fdb.CountReceiveOnlyChanged() +} + +func (s *DB) DropAllIndexIDs() error { + return s.forEachFolder(func(fdb *folderDB) error { + return fdb.DropAllIndexIDs() + }) +} + +func (s *DB) GetIndexID(folder string, device protocol.DeviceID) (protocol.IndexID, error) { + fdb, err := s.getFolderDB(folder, true) + if err != nil { + return 0, err + } + return fdb.GetIndexID(device) +} + +func (s *DB) SetIndexID(folder string, device protocol.DeviceID, id protocol.IndexID) error { + fdb, err := s.getFolderDB(folder, true) + if err != nil { + return err + } + return fdb.SetIndexID(device, id) +} + +func (s *DB) GetDeviceSequence(folder string, device protocol.DeviceID) (int64, error) { + fdb, err := s.getFolderDB(folder, false) + if errors.Is(err, errNoSuchFolder) { + return 0, nil + } + if err != nil { + return 0, err + } + return fdb.GetDeviceSequence(device) +} + +func (s *DB) DeleteMtime(folder, name string) error { + fdb, err := s.getFolderDB(folder, false) + if errors.Is(err, errNoSuchFolder) { + return nil + } + if err != nil { + return err + } + return fdb.DeleteMtime(name) +} + +func (s *DB) GetMtime(folder, name string) (ondisk, virtual time.Time) { + fdb, err := s.getFolderDB(folder, false) + if errors.Is(err, errNoSuchFolder) { + return time.Time{}, time.Time{} + } + if err != nil { + return time.Time{}, time.Time{} + } + return fdb.GetMtime(name) +} + +func (s *DB) PutMtime(folder, name string, ondisk, virtual time.Time) error { + fdb, err := s.getFolderDB(folder, true) + if err != nil { + return err + } + return fdb.PutMtime(name, ondisk, virtual) +} + +func (s *DB) DropDevice(device protocol.DeviceID) error { + return s.forEachFolder(func(fdb *folderDB) error { + return fdb.DropDevice(device) + }) +} + +// forEachFolder runs the function for each currently open folderDB, +// returning the first error that was encountered. +func (s *DB) forEachFolder(fn func(fdb *folderDB) error) error { + folders, err := s.ListFolders() + if err != nil { + return err + } + + var firstError error + for _, folder := range folders { + fdb, err := s.getFolderDB(folder, false) + if err != nil { + if firstError == nil { + firstError = err + } + continue + } + if err := fn(fdb); err != nil && firstError == nil { + firstError = err + } + } + return firstError +} diff --git a/internal/db/sqlite/db_global_test.go b/internal/db/sqlite/db_global_test.go new file mode 100644 index 000000000..3a3bae606 --- /dev/null +++ b/internal/db/sqlite/db_global_test.go @@ -0,0 +1,519 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package sqlite + +import ( + "slices" + "testing" + + "github.com/syncthing/syncthing/lib/config" + "github.com/syncthing/syncthing/lib/protocol" +) + +func TestNeed(t *testing.T) { + t.Helper() + + db, err := OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := db.Close(); err != nil { + t.Fatal(err) + } + }) + + // Some local files + var v protocol.Vector + baseV := v.Update(1) + newerV := baseV.Update(42) + files := []protocol.FileInfo{ + genFile("test1", 1, 0), // remote need + genFile("test2", 2, 0), // local need + genFile("test3", 3, 0), // global + } + files[0].Version = baseV + files[1].Version = baseV + files[2].Version = newerV + err = db.Update(folderID, protocol.LocalDeviceID, files) + if err != nil { + t.Fatal(err) + } + + // Some remote files + remote := []protocol.FileInfo{ + genFile("test2", 2, 100), // global + genFile("test3", 3, 101), // remote need + genFile("test4", 4, 102), // local need + } + remote[0].Version = newerV + remote[1].Version = baseV + remote[2].Version = newerV + err = db.Update(folderID, protocol.DeviceID{42}, remote) + if err != nil { + t.Fatal(err) + } + + // A couple are needed locally + localNeed := fiNames(mustCollect[protocol.FileInfo](t)(db.AllNeededGlobalFiles(folderID, protocol.LocalDeviceID, config.PullOrderAlphabetic, 0, 0))) + if !slices.Equal(localNeed, []string{"test2", "test4"}) { + t.Log(localNeed) + t.Fatal("bad local need") + } + + // Another couple are needed remotely + remoteNeed := fiNames(mustCollect[protocol.FileInfo](t)(db.AllNeededGlobalFiles(folderID, protocol.DeviceID{42}, config.PullOrderAlphabetic, 0, 0))) + if !slices.Equal(remoteNeed, []string{"test1", "test3"}) { + t.Log(remoteNeed) + t.Fatal("bad remote need") + } +} + +func TestDropRecalcsGlobal(t *testing.T) { + // When we drop a device we may get a new global + + t.Parallel() + + t.Run("DropAllFiles", func(t *testing.T) { + t.Parallel() + + testDropWithDropper(t, func(t *testing.T, db *DB) { + t.Helper() + if err := db.DropAllFiles(folderID, protocol.DeviceID{42}); err != nil { + t.Fatal(err) + } + }) + }) + + t.Run("DropDevice", func(t *testing.T) { + t.Parallel() + + testDropWithDropper(t, func(t *testing.T, db *DB) { + t.Helper() + if err := db.DropDevice(protocol.DeviceID{42}); err != nil { + t.Fatal(err) + } + }) + }) + + t.Run("DropFilesNamed", func(t *testing.T) { + t.Parallel() + + testDropWithDropper(t, func(t *testing.T, db *DB) { + t.Helper() + if err := db.DropFilesNamed(folderID, protocol.DeviceID{42}, []string{"test1", "test42"}); err != nil { + t.Fatal(err) + } + }) + }) +} + +func testDropWithDropper(t *testing.T, dropper func(t *testing.T, db *DB)) { + t.Helper() + + db, err := OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := db.Close(); err != nil { + t.Fatal(err) + } + }) + + // Some local files + err = db.Update(folderID, protocol.LocalDeviceID, []protocol.FileInfo{ + genFile("test1", 1, 0), + genFile("test2", 2, 0), + }) + if err != nil { + t.Fatal(err) + } + + // Some remote files + remote := []protocol.FileInfo{ + genFile("test1", 3, 0), + } + remote[0].Version = remote[0].Version.Update(42) + err = db.Update(folderID, protocol.DeviceID{42}, remote) + if err != nil { + t.Fatal(err) + } + + // Remote test1 wins as the global, verify. + count, err := db.CountGlobal(folderID) + if err != nil { + t.Fatal(err) + } + if count.Bytes != (2+3)*128<<10 { + t.Log(count) + t.Fatal("bad global size to begin with") + } + if g, ok, err := db.GetGlobalFile(folderID, "test1"); err != nil || !ok { + t.Fatal("missing global to begin with") + } else if g.Size != 3*128<<10 { + t.Fatal("remote test1 should be the global") + } + + // Now remove that remote device + dropper(t, db) + + // Our test1 should now be the global + count, err = db.CountGlobal(folderID) + if err != nil { + t.Fatal(err) + } + if count.Bytes != (1+2)*128<<10 { + t.Log(count) + t.Fatal("bad global size after drop") + } + if g, ok, err := db.GetGlobalFile(folderID, "test1"); err != nil || !ok { + t.Fatal("missing global after drop") + } else if g.Size != 1*128<<10 { + t.Fatal("local test1 should be the global") + } +} + +func TestNeedDeleted(t *testing.T) { + t.Parallel() + + db, err := OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := db.Close(); err != nil { + t.Fatal(err) + } + }) + + // Some local files + err = db.Update(folderID, protocol.LocalDeviceID, []protocol.FileInfo{ + genFile("test1", 1, 0), + genFile("test2", 2, 0), + }) + if err != nil { + t.Fatal(err) + } + + // A remote deleted file + remote := []protocol.FileInfo{ + genFile("test1", 1, 101), + } + remote[0].SetDeleted(42) + err = db.Update(folderID, protocol.DeviceID{42}, remote) + if err != nil { + t.Fatal(err) + } + + // We need the one deleted file + s, err := db.CountNeed(folderID, protocol.LocalDeviceID) + if err != nil { + t.Fatal(err) + } + if s.Bytes != 0 || s.Deleted != 1 { + t.Log(s) + t.Error("bad need") + } +} + +func TestDontNeedIgnored(t *testing.T) { + t.Parallel() + + db, err := OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := db.Close(); err != nil { + t.Fatal(err) + } + }) + + // A remote file + files := []protocol.FileInfo{ + genFile("test1", 1, 103), + } + err = db.Update(folderID, protocol.DeviceID{42}, files) + if err != nil { + t.Fatal(err) + } + + // Which we've ignored locally + files[0].SetIgnored() + err = db.Update(folderID, protocol.LocalDeviceID, files) + if err != nil { + t.Fatal(err) + } + + // We don't need it + s, err := db.CountNeed(folderID, protocol.LocalDeviceID) + if err != nil { + t.Fatal(err) + } + if s.Bytes != 0 || s.Files != 0 { + t.Log(s) + t.Error("bad need") + } + + // It shouldn't show up in the need list + names := mustCollect[protocol.FileInfo](t)(db.AllNeededGlobalFiles(folderID, protocol.LocalDeviceID, config.PullOrderAlphabetic, 0, 0)) + if len(names) != 0 { + t.Log(names) + t.Error("need no files") + } +} + +func TestRemoteDontNeedLocalIgnored(t *testing.T) { + t.Parallel() + + db, err := OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := db.Close(); err != nil { + t.Fatal(err) + } + }) + + // A local ignored file + file := genFile("test1", 1, 103) + file.SetIgnored() + files := []protocol.FileInfo{file} + err = db.Update(folderID, protocol.LocalDeviceID, files) + if err != nil { + t.Fatal(err) + } + + // Which the remote doesn't have (no update) + + // They don't need it + s, err := db.CountNeed(folderID, protocol.DeviceID{42}) + if err != nil { + t.Fatal(err) + } + if s.Bytes != 0 || s.Files != 0 { + t.Log(s) + t.Error("bad need") + } + + // It shouldn't show up in their need list + names := mustCollect[protocol.FileInfo](t)(db.AllNeededGlobalFiles(folderID, protocol.DeviceID{42}, config.PullOrderAlphabetic, 0, 0)) + if len(names) != 0 { + t.Log(names) + t.Error("need no files") + } +} + +func TestLocalDontNeedDeletedMissing(t *testing.T) { + t.Parallel() + + db, err := OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := db.Close(); err != nil { + t.Fatal(err) + } + }) + + // A remote deleted file + file := genFile("test1", 1, 103) + file.SetDeleted(42) + files := []protocol.FileInfo{file} + err = db.Update(folderID, protocol.DeviceID{42}, files) + if err != nil { + t.Fatal(err) + } + + // Which we don't have (no local update) + + // We don't need it + s, err := db.CountNeed(folderID, protocol.LocalDeviceID) + if err != nil { + t.Fatal(err) + } + if s.Bytes != 0 || s.Files != 0 || s.Deleted != 0 { + t.Log(s) + t.Error("bad need") + } + + // It shouldn't show up in the need list + names := mustCollect[protocol.FileInfo](t)(db.AllNeededGlobalFiles(folderID, protocol.LocalDeviceID, config.PullOrderAlphabetic, 0, 0)) + if len(names) != 0 { + t.Log(names) + t.Error("need no files") + } +} + +func TestRemoteDontNeedDeletedMissing(t *testing.T) { + t.Parallel() + + db, err := OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := db.Close(); err != nil { + t.Fatal(err) + } + }) + + // A local deleted file + file := genFile("test1", 1, 103) + file.SetDeleted(42) + files := []protocol.FileInfo{file} + err = db.Update(folderID, protocol.LocalDeviceID, files) + if err != nil { + t.Fatal(err) + } + + // Which the remote doesn't have (no local update) + + // They don't need it + s, err := db.CountNeed(folderID, protocol.DeviceID{42}) + if err != nil { + t.Fatal(err) + } + if s.Bytes != 0 || s.Files != 0 || s.Deleted != 0 { + t.Log(s) + t.Error("bad need") + } + + // It shouldn't show up in their need list + names := mustCollect[protocol.FileInfo](t)(db.AllNeededGlobalFiles(folderID, protocol.DeviceID{42}, config.PullOrderAlphabetic, 0, 0)) + if len(names) != 0 { + t.Log(names) + t.Error("need no files") + } + + // Another remote has announced it, but has set the invalid bit, + // presumably it's being ignored. + file = genFile("test1", 1, 103) + file.SetIgnored() + err = db.Update(folderID, protocol.DeviceID{43}, []protocol.FileInfo{file}) + if err != nil { + t.Fatal(err) + } + + // They don't need it, either + s, err = db.CountNeed(folderID, protocol.DeviceID{43}) + if err != nil { + t.Fatal(err) + } + if s.Bytes != 0 || s.Files != 0 || s.Deleted != 0 { + t.Log(s) + t.Error("bad need") + } + + // It shouldn't show up in their need list + names = mustCollect[protocol.FileInfo](t)(db.AllNeededGlobalFiles(folderID, protocol.DeviceID{42}, config.PullOrderAlphabetic, 0, 0)) + if len(names) != 0 { + t.Log(names) + t.Error("need no files") + } +} + +func TestNeedRemoteSymlinkAndDir(t *testing.T) { + t.Parallel() + + db, err := OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := db.Close(); err != nil { + t.Fatal(err) + } + }) + + // Two remote "specials", a symlink and a directory + var v protocol.Vector + v.Update(1) + files := []protocol.FileInfo{ + {Name: "sym", Type: protocol.FileInfoTypeSymlink, Sequence: 100, Version: v, Blocks: genBlocks("symlink", 0, 1)}, + {Name: "dir", Type: protocol.FileInfoTypeDirectory, Sequence: 101, Version: v}, + } + err = db.Update(folderID, protocol.DeviceID{42}, files) + if err != nil { + t.Fatal(err) + } + + // We need them + s, err := db.CountNeed(folderID, protocol.LocalDeviceID) + if err != nil { + t.Fatal(err) + } + if s.Directories != 1 || s.Symlinks != 1 { + t.Log(s) + t.Error("bad need") + } + + // They should be in the need list + names := mustCollect[protocol.FileInfo](t)(db.AllNeededGlobalFiles(folderID, protocol.LocalDeviceID, config.PullOrderAlphabetic, 0, 0)) + if len(names) != 2 { + t.Log(names) + t.Error("bad need") + } +} + +func TestNeedPagination(t *testing.T) { + t.Parallel() + + db, err := OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := db.Close(); err != nil { + t.Fatal(err) + } + }) + + // Several remote files + var v protocol.Vector + v.Update(1) + files := []protocol.FileInfo{ + genFile("test0", 1, 100), + genFile("test1", 1, 101), + genFile("test2", 1, 102), + genFile("test3", 1, 103), + genFile("test4", 1, 104), + genFile("test5", 1, 105), + genFile("test6", 1, 106), + genFile("test7", 1, 107), + genFile("test8", 1, 108), + genFile("test9", 1, 109), + } + err = db.Update(folderID, protocol.DeviceID{42}, files) + if err != nil { + t.Fatal(err) + } + + // We should get the first two + names := fiNames(mustCollect[protocol.FileInfo](t)(db.AllNeededGlobalFiles(folderID, protocol.LocalDeviceID, config.PullOrderAlphabetic, 2, 0))) + if !slices.Equal(names, []string{"test0", "test1"}) { + t.Log(names) + t.Error("bad need") + } + + // We should get the next three + names = fiNames(mustCollect[protocol.FileInfo](t)(db.AllNeededGlobalFiles(folderID, protocol.LocalDeviceID, config.PullOrderAlphabetic, 3, 2))) + if !slices.Equal(names, []string{"test2", "test3", "test4"}) { + t.Log(names) + t.Error("bad need") + } + + // We should get the last five + names = fiNames(mustCollect[protocol.FileInfo](t)(db.AllNeededGlobalFiles(folderID, protocol.LocalDeviceID, config.PullOrderAlphabetic, 5, 5))) + if !slices.Equal(names, []string{"test5", "test6", "test7", "test8", "test9"}) { + t.Log(names) + t.Error("bad need") + } +} diff --git a/internal/db/sqlite/db_indexid_test.go b/internal/db/sqlite/db_indexid_test.go new file mode 100644 index 000000000..82116734a --- /dev/null +++ b/internal/db/sqlite/db_indexid_test.go @@ -0,0 +1,81 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package sqlite + +import ( + "testing" + + "github.com/syncthing/syncthing/lib/protocol" +) + +func TestIndexIDs(t *testing.T) { + t.Parallel() + + db, err := OpenTemp() + if err != nil { + t.Fatal() + } + t.Cleanup(func() { + if err := db.Close(); err != nil { + t.Fatal(err) + } + }) + + t.Run("LocalDeviceID", func(t *testing.T) { + t.Parallel() + + localID, err := db.GetIndexID("foo", protocol.LocalDeviceID) + if err != nil { + t.Fatal(err) + } + if localID == 0 { + t.Fatal("should have been generated") + } + + again, err := db.GetIndexID("foo", protocol.LocalDeviceID) + if err != nil { + t.Fatal(err) + } + if again != localID { + t.Fatal("should get same again") + } + + other, err := db.GetIndexID("bar", protocol.LocalDeviceID) + if err != nil { + t.Fatal(err) + } + if other == localID { + t.Fatal("should not get same for other folder") + } + }) + + t.Run("OtherDeviceID", func(t *testing.T) { + t.Parallel() + + localID, err := db.GetIndexID("foo", protocol.DeviceID{42}) + if err != nil { + t.Fatal(err) + } + if localID != 0 { + t.Fatal("should have been zero") + } + + newID := protocol.NewIndexID() + if err := db.SetIndexID("foo", protocol.DeviceID{42}, newID); err != nil { + t.Fatal(err) + } + + again, err := db.GetIndexID("foo", protocol.DeviceID{42}) + if err != nil { + t.Fatal(err) + } + if again != newID { + t.Log(again, newID) + t.Fatal("should get the ID we set") + } + }) +} diff --git a/internal/db/sqlite/db_kv.go b/internal/db/sqlite/db_kv.go new file mode 100644 index 000000000..8e81d7d72 --- /dev/null +++ b/internal/db/sqlite/db_kv.go @@ -0,0 +1,78 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package sqlite + +import ( + "iter" + + "github.com/jmoiron/sqlx" + "github.com/syncthing/syncthing/internal/db" +) + +func (s *baseDB) GetKV(key string) ([]byte, error) { + var val []byte + if err := s.stmt(` + SELECT value FROM kv + WHERE key = ? + `).Get(&val, key); err != nil { + return nil, wrap(err) + } + return val, nil +} + +func (s *baseDB) PutKV(key string, val []byte) error { + s.updateLock.Lock() + defer s.updateLock.Unlock() + _, err := s.stmt(` + INSERT OR REPLACE INTO kv (key, value) + VALUES (?, ?) + `).Exec(key, val) + return wrap(err) +} + +func (s *baseDB) DeleteKV(key string) error { + s.updateLock.Lock() + defer s.updateLock.Unlock() + _, err := s.stmt(` + DELETE FROM kv WHERE key = ? + `).Exec(key) + return wrap(err) +} + +func (s *baseDB) PrefixKV(prefix string) (iter.Seq[db.KeyValue], func() error) { + var rows *sqlx.Rows + var err error + if prefix == "" { + rows, err = s.stmt(`SELECT key, value FROM kv`).Queryx() + } else { + end := prefixEnd(prefix) + rows, err = s.stmt(` + SELECT key, value FROM kv + WHERE key >= ? AND key < ? + `).Queryx(prefix, end) + } + if err != nil { + return func(_ func(db.KeyValue) bool) {}, func() error { return err } + } + + return func(yield func(db.KeyValue) bool) { + defer rows.Close() + for rows.Next() { + var key string + var val []byte + if err = rows.Scan(&key, &val); err != nil { + return + } + if !yield(db.KeyValue{Key: key, Value: val}) { + return + } + } + err = rows.Err() + }, func() error { + return err + } +} diff --git a/internal/db/sqlite/db_local_test.go b/internal/db/sqlite/db_local_test.go new file mode 100644 index 000000000..6932b7f3d --- /dev/null +++ b/internal/db/sqlite/db_local_test.go @@ -0,0 +1,191 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package sqlite + +import ( + "testing" + + "github.com/syncthing/syncthing/internal/itererr" + "github.com/syncthing/syncthing/lib/protocol" +) + +func TestBlocks(t *testing.T) { + t.Parallel() + + db, err := OpenTemp() + if err != nil { + t.Fatal() + } + t.Cleanup(func() { + if err := db.Close(); err != nil { + t.Fatal(err) + } + }) + + files := []protocol.FileInfo{ + { + Name: "file1", + Blocks: []protocol.BlockInfo{ + {Hash: []byte{1, 2, 3}, Offset: 0, Size: 42}, + {Hash: []byte{2, 3, 4}, Offset: 42, Size: 42}, + {Hash: []byte{3, 4, 5}, Offset: 84, Size: 42}, + }, + }, + { + Name: "file2", + Blocks: []protocol.BlockInfo{ + {Hash: []byte{2, 3, 4}, Offset: 0, Size: 42}, + {Hash: []byte{3, 4, 5}, Offset: 42, Size: 42}, + {Hash: []byte{4, 5, 6}, Offset: 84, Size: 42}, + }, + }, + } + + if err := db.Update("test", protocol.LocalDeviceID, files); err != nil { + t.Fatal(err) + } + + // Search for blocks + + vals, err := itererr.Collect(db.AllLocalBlocksWithHash(folderID, []byte{1, 2, 3})) + if err != nil { + t.Fatal(err) + } + if len(vals) != 1 { + t.Log(vals) + t.Fatal("expected one hit") + } else if vals[0].BlockIndex != 0 || vals[0].Offset != 0 || vals[0].Size != 42 { + t.Log(vals[0]) + t.Fatal("bad entry") + } + if vals[0].FileName != "file1" { + t.Fatal("should be file1") + } + + // Get the other blocks + + vals, err = itererr.Collect(db.AllLocalBlocksWithHash(folderID, []byte{3, 4, 5})) + if err != nil { + t.Fatal(err) + } + if len(vals) != 2 { + t.Log(vals) + t.Fatal("expected two hits") + } + // if vals[0].Index != 2 || vals[0].Offset != 84 || vals[0].Size != 42 { + // t.Log(vals[0]) + // t.Fatal("bad entry 1") + // } + // if vals[1].Index != 1 || vals[1].Offset != 42 || vals[1].Size != 42 { + // t.Log(vals[1]) + // t.Fatal("bad entry 2") + // } +} + +func TestBlocksDeleted(t *testing.T) { + t.Parallel() + + sdb, err := OpenTemp() + if err != nil { + t.Fatal() + } + t.Cleanup(func() { + if err := sdb.Close(); err != nil { + t.Fatal(err) + } + }) + + // Insert a file + file := genFile("foo", 1, 0) + if err := sdb.Update(folderID, protocol.LocalDeviceID, []protocol.FileInfo{file}); err != nil { + t.Fatal() + } + + // We should find one entry for the block hash + search := file.Blocks[0].Hash + es, err := itererr.Collect(sdb.AllLocalBlocksWithHash(folderID, search)) + if err != nil { + t.Fatal(err) + } + if len(es) != 1 { + t.Fatal("expected one hit") + } + + // Update the file with a new block hash + file.Blocks = genBlocks("foo", 42, 1) + if err := sdb.Update(folderID, protocol.LocalDeviceID, []protocol.FileInfo{file}); err != nil { + t.Fatal() + } + + // Searching for the old hash should yield no hits + if hits, err := itererr.Collect(sdb.AllLocalBlocksWithHash(folderID, search)); err != nil { + t.Fatal(err) + } else if len(hits) != 0 { + t.Log(hits) + t.Error("expected no hits") + } + + // Searching for the new hash should yield one hits + if hits, err := itererr.Collect(sdb.AllLocalBlocksWithHash(folderID, file.Blocks[0].Hash)); err != nil { + t.Fatal(err) + } else if len(hits) != 1 { + t.Log(hits) + t.Error("expected one hit") + } +} + +func TestRemoteSequence(t *testing.T) { + t.Parallel() + + sdb, err := OpenTemp() + if err != nil { + t.Fatal() + } + t.Cleanup(func() { + if err := sdb.Close(); err != nil { + t.Fatal(err) + } + }) + + // Insert a local file + file := genFile("foo", 1, 0) + if err := sdb.Update(folderID, protocol.LocalDeviceID, []protocol.FileInfo{file}); err != nil { + t.Fatal() + } + + // Insert several remote files + file = genFile("foo1", 1, 42) + if err := sdb.Update(folderID, protocol.DeviceID{42}, []protocol.FileInfo{file}); err != nil { + t.Fatal() + } + if err := sdb.Update(folderID, protocol.DeviceID{43}, []protocol.FileInfo{file}); err != nil { + t.Fatal() + } + file = genFile("foo2", 1, 43) + if err := sdb.Update(folderID, protocol.DeviceID{43}, []protocol.FileInfo{file}); err != nil { + t.Fatal() + } + if err := sdb.Update(folderID, protocol.DeviceID{44}, []protocol.FileInfo{file}); err != nil { + t.Fatal() + } + file = genFile("foo3", 1, 44) + if err := sdb.Update(folderID, protocol.DeviceID{44}, []protocol.FileInfo{file}); err != nil { + t.Fatal() + } + + // Verify remote sequences + seqs, err := sdb.RemoteSequences(folderID) + if err != nil { + t.Fatal(err) + } + if len(seqs) != 3 || seqs[protocol.DeviceID{42}] != 42 || + seqs[protocol.DeviceID{43}] != 43 || + seqs[protocol.DeviceID{44}] != 44 { + t.Log(seqs) + t.Error("bad seqs") + } +} diff --git a/internal/db/sqlite/db_mtimes_test.go b/internal/db/sqlite/db_mtimes_test.go new file mode 100644 index 000000000..71972eb5b --- /dev/null +++ b/internal/db/sqlite/db_mtimes_test.go @@ -0,0 +1,54 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package sqlite + +import ( + "testing" + "time" +) + +func TestMtimePairs(t *testing.T) { + t.Parallel() + + db, err := OpenTemp() + if err != nil { + t.Fatal() + } + t.Cleanup(func() { + if err := db.Close(); err != nil { + t.Fatal(err) + } + }) + + t0 := time.Now().Truncate(time.Second) + t1 := t0.Add(1234567890) + + // Set a pair + if err := db.PutMtime("foo", "bar", t0, t1); err != nil { + t.Fatal(err) + } + + // Check it + gt0, gt1 := db.GetMtime("foo", "bar") + if !gt0.Equal(t0) || !gt1.Equal(t1) { + t.Log(t0, gt0) + t.Log(t1, gt1) + t.Log("bad times") + } + + // Delete it + if err := db.DeleteMtime("foo", "bar"); err != nil { + t.Fatal(err) + } + + // Check it + gt0, gt1 = db.GetMtime("foo", "bar") + if !gt0.IsZero() || !gt1.IsZero() { + t.Log(gt0, gt1) + t.Log("bad times") + } +} diff --git a/internal/db/sqlite/db_open.go b/internal/db/sqlite/db_open.go new file mode 100644 index 000000000..072755f3c --- /dev/null +++ b/internal/db/sqlite/db_open.go @@ -0,0 +1,143 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package sqlite + +import ( + "os" + "path/filepath" + "sync" + "time" + + "github.com/syncthing/syncthing/internal/db" +) + +const maxDBConns = 16 + +type DB struct { + pathBase string + deleteRetention time.Duration + + *baseDB + + folderDBsMut sync.RWMutex + folderDBs map[string]*folderDB + folderDBOpener func(folder, path string, deleteRetention time.Duration) (*folderDB, error) +} + +var _ db.DB = (*DB)(nil) + +type Option func(*DB) + +func WithDeleteRetention(d time.Duration) Option { + return func(s *DB) { + s.deleteRetention = d + } +} + +func Open(path string, opts ...Option) (*DB, error) { + pragmas := []string{ + "journal_mode = WAL", + "optimize = 0x10002", + "auto_vacuum = INCREMENTAL", + "default_temp_store = MEMORY", + "temp_store = MEMORY", + } + schemas := []string{ + "sql/schema/common/*", + "sql/schema/main/*", + } + migrations := []string{ + "sql/migrations/common/*", + "sql/migrations/main/*", + } + + _ = os.MkdirAll(path, 0o700) + mainPath := filepath.Join(path, "main.db") + mainBase, err := openBase(mainPath, maxDBConns, pragmas, schemas, migrations) + if err != nil { + return nil, err + } + + db := &DB{ + pathBase: path, + baseDB: mainBase, + folderDBs: make(map[string]*folderDB), + folderDBOpener: openFolderDB, + } + + for _, opt := range opts { + opt(db) + } + + return db, nil +} + +// Open the database with options suitable for the migration inserts. This +// is not a safe mode of operation for normal processing, use only for bulk +// inserts with a close afterwards. +func OpenForMigration(path string) (*DB, error) { + pragmas := []string{ + "journal_mode = OFF", + "default_temp_store = MEMORY", + "temp_store = MEMORY", + "foreign_keys = 0", + "synchronous = 0", + "locking_mode = EXCLUSIVE", + } + schemas := []string{ + "sql/schema/common/*", + "sql/schema/main/*", + } + migrations := []string{ + "sql/migrations/common/*", + "sql/migrations/main/*", + } + + _ = os.MkdirAll(path, 0o700) + mainPath := filepath.Join(path, "main.db") + mainBase, err := openBase(mainPath, 1, pragmas, schemas, migrations) + if err != nil { + return nil, err + } + + db := &DB{ + pathBase: path, + baseDB: mainBase, + folderDBs: make(map[string]*folderDB), + folderDBOpener: openFolderDBForMigration, + } + + // // Touch device IDs that should always exist and have a low index + // // numbers, and will never change + // db.localDeviceIdx, _ = db.deviceIdxLocked(protocol.LocalDeviceID) + // db.tplInput["LocalDeviceIdx"] = db.localDeviceIdx + + return db, nil +} + +func OpenTemp() (*DB, error) { + // SQLite has a memory mode, but it works differently with concurrency + // compared to what we need with the WAL mode. So, no memory databases + // for now. + dir, err := os.MkdirTemp("", "syncthing-db") + if err != nil { + return nil, wrap(err) + } + path := filepath.Join(dir, "db") + l.Debugln("Test DB in", path) + return Open(path) +} + +func (s *DB) Close() error { + s.folderDBsMut.Lock() + defer s.folderDBsMut.Unlock() + for folder, fdb := range s.folderDBs { + fdb.Close() + delete(s.folderDBs, folder) + } + return wrap(s.baseDB.Close()) +} diff --git a/internal/db/sqlite/db_open_cgo.go b/internal/db/sqlite/db_open_cgo.go new file mode 100644 index 000000000..db86c6b4c --- /dev/null +++ b/internal/db/sqlite/db_open_cgo.go @@ -0,0 +1,18 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +//go:build cgo + +package sqlite + +import ( + _ "github.com/mattn/go-sqlite3" // register sqlite3 database driver +) + +const ( + dbDriver = "sqlite3" + commonOptions = "_fk=true&_rt=true&_cache_size=-65536&_sync=1&_txlock=immediate" +) diff --git a/internal/db/sqlite/db_open_nocgo.go b/internal/db/sqlite/db_open_nocgo.go new file mode 100644 index 000000000..ea33222e4 --- /dev/null +++ b/internal/db/sqlite/db_open_nocgo.go @@ -0,0 +1,23 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +//go:build !cgo && !wazero + +package sqlite + +import ( + "github.com/syncthing/syncthing/lib/build" + _ "modernc.org/sqlite" // register sqlite database driver +) + +const ( + dbDriver = "sqlite" + commonOptions = "_pragma=foreign_keys(1)&_pragma=recursive_triggers(1)&_pragma=cache_size(-65536)&_pragma=synchronous(1)" +) + +func init() { + build.AddTag("modernc-sqlite") +} diff --git a/internal/db/sqlite/db_prepared.go b/internal/db/sqlite/db_prepared.go new file mode 100644 index 000000000..b06281ea7 --- /dev/null +++ b/internal/db/sqlite/db_prepared.go @@ -0,0 +1,44 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package sqlite + +import "github.com/jmoiron/sqlx" + +type txPreparedStmts struct { + *sqlx.Tx + stmts map[string]*sqlx.Stmt +} + +func (p *txPreparedStmts) Preparex(query string) (*sqlx.Stmt, error) { + if p.stmts == nil { + p.stmts = make(map[string]*sqlx.Stmt) + } + stmt, ok := p.stmts[query] + if ok { + return stmt, nil + } + stmt, err := p.Tx.Preparex(query) + if err != nil { + return nil, wrap(err) + } + p.stmts[query] = stmt + return stmt, nil +} + +func (p *txPreparedStmts) Commit() error { + for _, s := range p.stmts { + s.Close() + } + return p.Tx.Commit() +} + +func (p *txPreparedStmts) Rollback() error { + for _, s := range p.stmts { + s.Close() + } + return p.Tx.Rollback() +} diff --git a/internal/db/sqlite/db_service.go b/internal/db/sqlite/db_service.go new file mode 100644 index 000000000..38695785e --- /dev/null +++ b/internal/db/sqlite/db_service.go @@ -0,0 +1,196 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package sqlite + +import ( + "context" + "fmt" + "time" + + "github.com/jmoiron/sqlx" + "github.com/syncthing/syncthing/internal/db" + "github.com/thejerf/suture/v4" +) + +const ( + internalMetaPrefix = "dbsvc" + lastMaintKey = "lastMaint" + defaultDeleteRetention = 180 * 24 * time.Hour + minDeleteRetention = 24 * time.Hour +) + +func (s *DB) Service(maintenanceInterval time.Duration) suture.Service { + return newService(s, maintenanceInterval) +} + +type Service struct { + sdb *DB + maintenanceInterval time.Duration + internalMeta *db.Typed +} + +func (s *Service) String() string { + return fmt.Sprintf("sqlite.service@%p", s) +} + +func newService(sdb *DB, maintenanceInterval time.Duration) *Service { + return &Service{ + sdb: sdb, + maintenanceInterval: maintenanceInterval, + internalMeta: db.NewTyped(sdb, internalMetaPrefix), + } +} + +func (s *Service) Serve(ctx context.Context) error { + // Run periodic maintenance + + // Figure out when we last ran maintenance and schedule accordingly. If + // it was never, do it now. + lastMaint, _, _ := s.internalMeta.Time(lastMaintKey) + nextMaint := lastMaint.Add(s.maintenanceInterval) + wait := time.Until(nextMaint) + if wait < 0 { + wait = time.Minute + } + l.Debugln("Next periodic run in", wait) + + timer := time.NewTimer(wait) + for { + select { + case <-ctx.Done(): + return ctx.Err() + case <-timer.C: + } + + if err := s.periodic(ctx); err != nil { + return wrap(err) + } + + timer.Reset(s.maintenanceInterval) + l.Debugln("Next periodic run in", s.maintenanceInterval) + _ = s.internalMeta.PutTime(lastMaintKey, time.Now()) + } +} + +func (s *Service) periodic(ctx context.Context) error { + t0 := time.Now() + l.Debugln("Periodic start") + + t1 := time.Now() + defer func() { l.Debugln("Periodic done in", time.Since(t1), "+", t1.Sub(t0)) }() + + s.sdb.updateLock.Lock() + err := tidy(ctx, s.sdb.sql) + s.sdb.updateLock.Unlock() + if err != nil { + return err + } + + return wrap(s.sdb.forEachFolder(func(fdb *folderDB) error { + fdb.updateLock.Lock() + defer fdb.updateLock.Unlock() + + if err := garbageCollectOldDeletedLocked(fdb); err != nil { + return wrap(err) + } + if err := garbageCollectBlocklistsAndBlocksLocked(ctx, fdb); err != nil { + return wrap(err) + } + return tidy(ctx, fdb.sql) + })) +} + +func tidy(ctx context.Context, db *sqlx.DB) error { + conn, err := db.Conn(ctx) + if err != nil { + return wrap(err) + } + defer conn.Close() + _, _ = conn.ExecContext(ctx, `ANALYZE`) + _, _ = conn.ExecContext(ctx, `PRAGMA optimize`) + _, _ = conn.ExecContext(ctx, `PRAGMA incremental_vacuum`) + _, _ = conn.ExecContext(ctx, `PRAGMA journal_size_limit = 8388608`) + _, _ = conn.ExecContext(ctx, `PRAGMA wal_checkpoint(TRUNCATE)`) + return nil +} + +func garbageCollectOldDeletedLocked(fdb *folderDB) error { + if fdb.deleteRetention <= 0 { + l.Debugln(fdb.baseName, "delete retention is infinite, skipping cleanup") + return nil + } + + // Remove deleted files that are marked as not needed (we have processed + // them) and they were deleted more than MaxDeletedFileAge ago. + l.Debugln(fdb.baseName, "forgetting deleted files older than", fdb.deleteRetention) + res, err := fdb.stmt(` + DELETE FROM files + WHERE deleted AND modified < ? AND local_flags & {{.FlagLocalNeeded}} == 0 + `).Exec(time.Now().Add(-fdb.deleteRetention).UnixNano()) + if err != nil { + return wrap(err) + } + if aff, err := res.RowsAffected(); err == nil { + l.Debugln(fdb.baseName, "removed old deleted file records:", aff) + } + return nil +} + +func garbageCollectBlocklistsAndBlocksLocked(ctx context.Context, fdb *folderDB) error { + // Remove all blocklists not referred to by any files and, by extension, + // any blocks not referred to by a blocklist. This is an expensive + // operation when run normally, especially if there are a lot of blocks + // to collect. + // + // We make this orders of magnitude faster by disabling foreign keys for + // the transaction and doing the cleanup manually. This requires using + // an explicit connection and disabling foreign keys before starting the + // transaction. We make sure to clean up on the way out. + + conn, err := fdb.sql.Connx(ctx) + if err != nil { + return wrap(err) + } + defer conn.Close() + + if _, err := conn.ExecContext(ctx, `PRAGMA foreign_keys = 0`); err != nil { + return wrap(err) + } + defer func() { //nolint:contextcheck + _, _ = conn.ExecContext(context.Background(), `PRAGMA foreign_keys = 1`) + }() + + tx, err := conn.BeginTxx(ctx, nil) + if err != nil { + return wrap(err) + } + defer tx.Rollback() //nolint:errcheck + + if res, err := tx.ExecContext(ctx, ` + DELETE FROM blocklists + WHERE NOT EXISTS ( + SELECT 1 FROM files WHERE files.blocklist_hash = blocklists.blocklist_hash + )`); err != nil { + return wrap(err, "delete blocklists") + } else if shouldDebug() { + rows, err := res.RowsAffected() + l.Debugln(fdb.baseName, "blocklist GC:", rows, err) + } + + if res, err := tx.ExecContext(ctx, ` + DELETE FROM blocks + WHERE NOT EXISTS ( + SELECT 1 FROM blocklists WHERE blocklists.blocklist_hash = blocks.blocklist_hash + )`); err != nil { + return wrap(err, "delete blocks") + } else if shouldDebug() { + rows, err := res.RowsAffected() + l.Debugln(fdb.baseName, "blocks GC:", rows, err) + } + + return wrap(tx.Commit()) +} diff --git a/internal/db/sqlite/db_stats.go b/internal/db/sqlite/db_stats.go new file mode 100644 index 000000000..06925c029 --- /dev/null +++ b/internal/db/sqlite/db_stats.go @@ -0,0 +1,69 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package sqlite + +type DatabaseStatistics struct { + Name string `json:"name"` + FolderID string `json:"folderID,omitempty"` + Tables []TableStatistics `json:"tables"` + Total TableStatistics `json:"total"` + Children []DatabaseStatistics `json:"children,omitempty"` +} + +type TableStatistics struct { + Name string `json:"name,omitempty"` + Size int64 `json:"size"` + Unused int64 `json:"unused"` +} + +func (s *DB) Statistics() (*DatabaseStatistics, error) { + ts, total, err := s.tableStats() + if err != nil { + return nil, wrap(err) + } + ds := DatabaseStatistics{ + Name: s.baseName, + Tables: ts, + Total: total, + } + + err = s.forEachFolder(func(fdb *folderDB) error { + tables, total, err := fdb.tableStats() + if err != nil { + return wrap(err) + } + ds.Children = append(ds.Children, DatabaseStatistics{ + Name: fdb.baseName, + FolderID: fdb.folderID, + Tables: tables, + Total: total, + }) + return nil + }) + if err != nil { + return nil, wrap(err) + } + + return &ds, nil +} + +func (s *baseDB) tableStats() ([]TableStatistics, TableStatistics, error) { + var stats []TableStatistics + if err := s.stmt(` + SELECT name, pgsize AS size, unused FROM dbstat + WHERE aggregate=true + ORDER BY name + `).Select(&stats); err != nil { + return nil, TableStatistics{}, wrap(err) + } + var total TableStatistics + for _, s := range stats { + total.Size += s.Size + total.Unused += s.Unused + } + return stats, total, nil +} diff --git a/internal/db/sqlite/db_test.go b/internal/db/sqlite/db_test.go new file mode 100644 index 000000000..e1ccc44a3 --- /dev/null +++ b/internal/db/sqlite/db_test.go @@ -0,0 +1,1232 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package sqlite + +import ( + "context" + "crypto/sha256" + "encoding/binary" + "errors" + "iter" + "path/filepath" + "sync" + "testing" + "time" + + "github.com/syncthing/syncthing/internal/db" + "github.com/syncthing/syncthing/internal/itererr" + "github.com/syncthing/syncthing/internal/timeutil" + "github.com/syncthing/syncthing/lib/config" + "github.com/syncthing/syncthing/lib/protocol" +) + +const ( + folderID = "test" + blockSize = 128 << 10 + dirSize = 128 +) + +func TestBasics(t *testing.T) { + t.Parallel() + + sdb, err := OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := sdb.Close(); err != nil { + t.Fatal(err) + } + }) + + // Some local files + local := []protocol.FileInfo{ + genFile("test1", 1, 0), + genDir("test2", 0), + genFile("test2/a", 2, 0), + genFile("test2/b", 3, 0), + } + err = sdb.Update(folderID, protocol.LocalDeviceID, local) + if err != nil { + t.Fatal(err) + } + + // Some remote files + remote := []protocol.FileInfo{ + genFile("test3", 3, 101), + genFile("test4", 4, 102), + genFile("test1", 5, 103), + } + // All newer than the local ones + for i := range remote { + remote[i].Version = remote[i].Version.Update(42) + } + err = sdb.Update(folderID, protocol.DeviceID{42}, remote) + if err != nil { + t.Fatal(err) + } + const ( + localSize = (1+2+3)*blockSize + dirSize + remoteSize = (3 + 4 + 5) * blockSize + globalSize = (2+3+3+4+5)*blockSize + dirSize + needSizeLocal = remoteSize + needSizeRemote = (2+3)*blockSize + dirSize + ) + + t.Run("SchemaVersion", func(t *testing.T) { + ver, err := sdb.getAppliedSchemaVersion() + if err != nil { + t.Fatal(err) + } + if ver.SchemaVersion != currentSchemaVersion { + t.Log(ver) + t.Error("should be version 1") + } + if d := time.Since(ver.AppliedTime()); d > time.Minute || d < 0 { + t.Log(ver) + t.Error("suspicious applied tim") + } + }) + + t.Run("Local", func(t *testing.T) { + t.Parallel() + + fi, ok, err := sdb.GetDeviceFile(folderID, protocol.LocalDeviceID, "test2/a") // exists + if err != nil { + t.Fatal(err) + } + if !ok { + t.Fatal("not found") + } + if fi.Name != filepath.FromSlash("test2/a") { + t.Fatal("should have got test2/a") + } + if len(fi.Blocks) != 2 { + t.Fatal("expected two blocks") + } + + _, ok, err = sdb.GetDeviceFile(folderID, protocol.LocalDeviceID, "test3") // does not exist + if err != nil { + t.Fatal(err) + } + if ok { + t.Fatal("should be not found") + } + }) + + t.Run("Global", func(t *testing.T) { + t.Parallel() + + fi, ok, err := sdb.GetGlobalFile(folderID, "test1") + if err != nil { + t.Fatal(err) + } + if !ok { + t.Fatal("not found") + } + if fi.Size != 5*blockSize { + t.Fatal("should be the remote file") + } + }) + + t.Run("AllLocal", func(t *testing.T) { + t.Parallel() + + have := mustCollect[protocol.FileInfo](t)(sdb.AllLocalFiles(folderID, protocol.LocalDeviceID)) + if len(have) != 4 { + t.Log(have) + t.Error("expected four files") + } + have = mustCollect[protocol.FileInfo](t)(sdb.AllLocalFiles(folderID, protocol.DeviceID{42})) + if len(have) != 3 { + t.Log(have) + t.Error("expected three files") + } + }) + + t.Run("AllNeededNamesLocal", func(t *testing.T) { + t.Parallel() + + need := fiNames(mustCollect[protocol.FileInfo](t)(sdb.AllNeededGlobalFiles(folderID, protocol.LocalDeviceID, config.PullOrderAlphabetic, 0, 0))) + if len(need) != 3 || need[0] != "test1" { + t.Log(need) + t.Error("expected three files, ordered alphabetically") + } + + need = fiNames(mustCollect[protocol.FileInfo](t)(sdb.AllNeededGlobalFiles(folderID, protocol.LocalDeviceID, config.PullOrderAlphabetic, 1, 0))) + if len(need) != 1 || need[0] != "test1" { + t.Log(need) + t.Error("expected one file, limited, ordered alphabetically") + } + need = fiNames(mustCollect[protocol.FileInfo](t)(sdb.AllNeededGlobalFiles(folderID, protocol.LocalDeviceID, config.PullOrderLargestFirst, 0, 0))) + if len(need) != 3 || need[0] != "test1" { // largest + t.Log(need) + t.Error("expected three files, ordered largest to smallest") + } + need = fiNames(mustCollect[protocol.FileInfo](t)(sdb.AllNeededGlobalFiles(folderID, protocol.LocalDeviceID, config.PullOrderSmallestFirst, 0, 0))) + if len(need) != 3 || need[0] != "test3" { // smallest + t.Log(need) + t.Error("expected three files, ordered smallest to largest") + } + + need = fiNames(mustCollect[protocol.FileInfo](t)(sdb.AllNeededGlobalFiles(folderID, protocol.LocalDeviceID, config.PullOrderNewestFirst, 0, 0))) + if len(need) != 3 || need[0] != "test1" { // newest + t.Log(need) + t.Error("expected three files, ordered newest to oldest") + } + need = fiNames(mustCollect[protocol.FileInfo](t)(sdb.AllNeededGlobalFiles(folderID, protocol.LocalDeviceID, config.PullOrderOldestFirst, 0, 0))) + if len(need) != 3 || need[0] != "test3" { // oldest + t.Log(need) + t.Error("expected three files, ordered oldest to newest") + } + }) + + t.Run("LocalSize", func(t *testing.T) { + t.Parallel() + + // Local device + + c, err := sdb.CountLocal(folderID, protocol.LocalDeviceID) + if err != nil { + t.Fatal(err) + } + if c.Files != 3 { + t.Log(c) + t.Error("one file expected") + } + if c.Directories != 1 { + t.Log(c) + t.Error("one directory expected") + } + if c.Bytes != localSize { + t.Log(c) + t.Error("size unexpected") + } + + // Other device + + c, err = sdb.CountLocal(folderID, protocol.DeviceID{42}) + if err != nil { + t.Fatal(err) + } + if c.Files != 3 { + t.Log(c) + t.Error("three files expected") + } + if c.Directories != 0 { + t.Log(c) + t.Error("no directories expected") + } + if c.Bytes != remoteSize { + t.Log(c) + t.Error("size unexpected") + } + }) + + t.Run("GlobalSize", func(t *testing.T) { + t.Parallel() + + c, err := sdb.CountGlobal(folderID) + if err != nil { + t.Fatal(err) + } + if c.Files != 5 { + t.Log(c) + t.Error("five files expected") + } + if c.Directories != 1 { + t.Log(c) + t.Error("one directory expected") + } + if c.Bytes != int64(globalSize) { + t.Log(c) + t.Error("size unexpected") + } + }) + + t.Run("NeedSizeLocal", func(t *testing.T) { + t.Parallel() + + c, err := sdb.CountNeed(folderID, protocol.LocalDeviceID) + if err != nil { + t.Fatal(err) + } + if c.Files != 3 { + t.Log(c) + t.Error("three files expected") + } + if c.Directories != 0 { + t.Log(c) + t.Error("no directories expected") + } + if c.Bytes != needSizeLocal { + t.Log(c) + t.Error("size unexpected") + } + }) + + t.Run("NeedSizeRemote", func(t *testing.T) { + t.Parallel() + + c, err := sdb.CountNeed(folderID, protocol.DeviceID{42}) + if err != nil { + t.Fatal(err) + } + if c.Files != 2 { + t.Log(c) + t.Error("two files expected") + } + if c.Directories != 1 { + t.Log(c) + t.Error("one directory expected") + } + if c.Bytes != needSizeRemote { + t.Log(c) + t.Error("size unexpected") + } + }) + + t.Run("Folders", func(t *testing.T) { + t.Parallel() + + folders, err := sdb.ListFolders() + if err != nil { + t.Fatal(err) + } + if len(folders) != 1 || folders[0] != folderID { + t.Log(folders) + t.Error("expected one folder") + } + }) + + t.Run("DevicesForFolder", func(t *testing.T) { + t.Parallel() + + devs, err := sdb.ListDevicesForFolder("test") + if err != nil { + t.Fatal(err) + } + if len(devs) != 1 || devs[0] != (protocol.DeviceID{42}) { + t.Log(devs) + t.Error("expected one device") + } + }) + + t.Run("Sequence", func(t *testing.T) { + t.Parallel() + + iid, err := sdb.GetIndexID(folderID, protocol.LocalDeviceID) + if err != nil { + t.Fatal(err) + } + if iid == 0 { + t.Log(iid) + t.Fatal("expected index ID") + } + + if seq, err := sdb.GetDeviceSequence(folderID, protocol.LocalDeviceID); err != nil { + t.Fatal(err) + } else if seq != 4 { + t.Log(seq) + t.Error("expected local sequence to match number of files inserted") + } + + if seq, err := sdb.GetDeviceSequence(folderID, protocol.DeviceID{42}); err != nil { + t.Fatal(err) + } else if seq != 103 { + t.Log(seq) + t.Error("expected remote sequence to match highest sent") + } + + // Non-existent should be zero and no error + if seq, err := sdb.GetDeviceSequence("trolol", protocol.LocalDeviceID); err != nil { + t.Fatal(err) + } else if seq != 0 { + t.Log(seq) + t.Error("expected zero sequence") + } + if seq, err := sdb.GetDeviceSequence("trolol", protocol.DeviceID{42}); err != nil { + t.Fatal(err) + } else if seq != 0 { + t.Log(seq) + t.Error("expected zero sequence") + } + if seq, err := sdb.GetDeviceSequence(folderID, protocol.DeviceID{99}); err != nil { + t.Fatal(err) + } else if seq != 0 { + t.Log(seq) + t.Error("expected zero sequence") + } + }) + + t.Run("AllGlobalPrefix", func(t *testing.T) { + t.Parallel() + + vals := mustCollect[db.FileMetadata](t)(sdb.AllGlobalFilesPrefix(folderID, "test2")) + + // Vals should be test2, test2/a, test2/b + if len(vals) != 3 { + t.Log(vals) + t.Error("expected three items") + } else if vals[0].Name != "test2" { + t.Error(vals) + } + + // Empty prefix should be all the files + vals = mustCollect[db.FileMetadata](t)(sdb.AllGlobalFilesPrefix(folderID, "")) + if len(vals) != 6 { + t.Log(vals) + t.Error("expected six items") + } + }) + + t.Run("AllLocalPrefix", func(t *testing.T) { + t.Parallel() + + vals := mustCollect[protocol.FileInfo](t)(sdb.AllLocalFilesWithPrefix(folderID, protocol.LocalDeviceID, "test2")) + + // Vals should be test2, test2/a, test2/b + if len(vals) != 3 { + t.Log(vals) + t.Error("expected three items") + } else if vals[0].Name != "test2" { + t.Error(vals) + } + + // Empty prefix should be all the files + vals = mustCollect[protocol.FileInfo](t)(sdb.AllLocalFilesWithPrefix(folderID, protocol.LocalDeviceID, "")) + + if len(vals) != 4 { + t.Log(vals) + t.Error("expected four items") + } + }) + + t.Run("AllLocalSequenced", func(t *testing.T) { + t.Parallel() + + vals := mustCollect[protocol.FileInfo](t)(sdb.AllLocalFilesBySequence(folderID, protocol.LocalDeviceID, 3, 0)) + + // Vals should be test2/a, test2/b + if len(vals) != 2 { + t.Log(vals) + t.Error("expected three items") + } else if vals[0].Name != filepath.FromSlash("test2/a") || vals[0].Sequence != 3 { + t.Error(vals) + } + }) +} + +func TestPrefixGlobbing(t *testing.T) { + t.Parallel() + + sdb, err := OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := sdb.Close(); err != nil { + t.Fatal(err) + } + }) + + // Some local files + local := []protocol.FileInfo{ + genFile("test1", 1, 0), + genDir("test2", 0), + genFile("test2/a", 2, 0), + genDir("test2/b", 0), + genFile("test2/b/c", 3, 0), + } + err = sdb.Update(folderID, protocol.LocalDeviceID, local) + if err != nil { + t.Fatal(err) + } + + vals := mustCollect[protocol.FileInfo](t)(sdb.AllLocalFilesWithPrefix(folderID, protocol.LocalDeviceID, "test2")) + + // Vals should be test2, test2/a, test2/b, test2/b/c + if len(vals) != 4 { + t.Log(vals) + t.Error("expected four items") + } else if vals[0].Name != "test2" || vals[3].Name != filepath.FromSlash("test2/b/c") { + t.Error(vals) + } + + // Empty prefix should be all the files + vals = mustCollect[protocol.FileInfo](t)(sdb.AllLocalFilesWithPrefix(folderID, protocol.LocalDeviceID, "")) + + if len(vals) != 5 { + t.Log(vals) + t.Error("expected five items") + } + + // Same as partial prefix + vals = mustCollect[protocol.FileInfo](t)(sdb.AllLocalFilesWithPrefix(folderID, protocol.LocalDeviceID, "tes")) + + if len(vals) != 5 { + t.Log(vals) + t.Error("expected five items") + } + + // Prefix should be case sensitive, so no match here + vals = mustCollect[protocol.FileInfo](t)(sdb.AllLocalFilesWithPrefix(folderID, protocol.LocalDeviceID, "tEsT2")) + + if len(vals) != 0 { + t.Log(vals) + t.Error("expected no items") + } + + // Subdir should match + vals = mustCollect[protocol.FileInfo](t)(sdb.AllLocalFilesWithPrefix(folderID, protocol.LocalDeviceID, "test2/b")) + + if len(vals) != 2 { + t.Log(vals) + t.Error("expected two items") + } +} + +func TestPrefixGlobbingStar(t *testing.T) { + t.Parallel() + + sdb, err := OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := sdb.Close(); err != nil { + t.Fatal(err) + } + }) + + // Some local files + local := []protocol.FileInfo{ + genFile("test1a", 1, 0), + genFile("test*a", 2, 0), + genFile("test2a", 3, 0), + } + err = sdb.Update(folderID, protocol.LocalDeviceID, local) + if err != nil { + t.Fatal(err) + } + + vals := mustCollect[protocol.FileInfo](t)(sdb.AllLocalFilesWithPrefix(folderID, protocol.LocalDeviceID, "test*a")) + + // Vals should be test*a + if len(vals) != 1 { + t.Log(vals) + t.Error("expected one item") + } else if vals[0].Name != "test*a" { + t.Error(vals) + } +} + +func TestAvailability(t *testing.T) { + db, err := OpenTemp() + if err != nil { + t.Fatal(err) + } + + const folderID = "test" + + // Some local files + err = db.Update(folderID, protocol.LocalDeviceID, []protocol.FileInfo{ + genFile("test1", 1, 0), + genFile("test2", 2, 0), + }) + if err != nil { + t.Fatal(err) + } + + // Some remote files + err = db.Update(folderID, protocol.DeviceID{42}, []protocol.FileInfo{ + genFile("test2", 2, 1), + genFile("test3", 3, 2), + }) + if err != nil { + t.Fatal(err) + } + + // Further remote files + err = db.Update(folderID, protocol.DeviceID{45}, []protocol.FileInfo{ + genFile("test3", 3, 1), + genFile("test4", 4, 2), + }) + if err != nil { + t.Fatal(err) + } + + a, err := db.GetGlobalAvailability(folderID, "test1") + if err != nil { + t.Fatal(err) + } + if len(a) != 0 { + t.Log(a) + t.Error("expected no availability (only local)") + } + + a, err = db.GetGlobalAvailability(folderID, "test2") + if err != nil { + t.Fatal(err) + } + if len(a) != 1 || a[0] != (protocol.DeviceID{42}) { + t.Log(a) + t.Error("expected one availability (only 42)") + } + + a, err = db.GetGlobalAvailability(folderID, "test3") + if err != nil { + t.Fatal(err) + } + if len(a) != 2 || a[0] != (protocol.DeviceID{42}) || a[1] != (protocol.DeviceID{45}) { + t.Log(a) + t.Error("expected two availabilities (both remotes)") + } + + if err := db.Close(); err != nil { + t.Fatal(err) + } +} + +func TestDropFilesNamed(t *testing.T) { + db, err := OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := db.Close(); err != nil { + t.Fatal(err) + } + }) + + const folderID = "test" + + // Some local files + err = db.Update(folderID, protocol.LocalDeviceID, []protocol.FileInfo{ + genFile("test1", 1, 0), + genFile("test2", 2, 0), + }) + if err != nil { + t.Fatal(err) + } + + // Drop test1 + if err := db.DropFilesNamed(folderID, protocol.LocalDeviceID, []string{"test1"}); err != nil { + t.Fatal(err) + } + + // Check + if _, ok, err := db.GetDeviceFile(folderID, protocol.LocalDeviceID, "test1"); err != nil || ok { + t.Log(err, ok) + t.Error("expected to not exist") + } + if c, err := db.CountLocal(folderID, protocol.LocalDeviceID); err != nil { + t.Fatal(err) + } else if c.Files != 1 { + t.Log(c) + t.Error("expected count to be one") + } + if _, ok, err := db.GetDeviceFile(folderID, protocol.LocalDeviceID, "test2"); err != nil || !ok { + t.Log(err, ok) + t.Error("expected to exist") + } +} + +func TestDropFolder(t *testing.T) { + db, err := OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := db.Close(); err != nil { + t.Fatal(err) + } + }) + + // Some local files + + // Folder A + err = db.Update("a", protocol.LocalDeviceID, []protocol.FileInfo{ + genFile("test1", 1, 0), + genFile("test2", 2, 0), + }) + if err != nil { + t.Fatal(err) + } + + // Folder B + err = db.Update("b", protocol.LocalDeviceID, []protocol.FileInfo{ + genFile("test1", 1, 0), + genFile("test2", 2, 0), + }) + if err != nil { + t.Fatal(err) + } + + // Drop A + if err := db.DropFolder("a"); err != nil { + t.Fatal(err) + } + + // Check + if _, ok, err := db.GetDeviceFile("a", protocol.LocalDeviceID, "test1"); err != nil || ok { + t.Log(err, ok) + t.Error("expected to not exist") + } + if c, err := db.CountLocal("a", protocol.LocalDeviceID); err != nil { + t.Fatal(err) + } else if c.Files != 0 { + t.Log(c) + t.Error("expected count to be zero") + } + + if _, ok, err := db.GetDeviceFile("b", protocol.LocalDeviceID, "test1"); err != nil || !ok { + t.Log(err, ok) + t.Error("expected to exist") + } + if c, err := db.CountLocal("b", protocol.LocalDeviceID); err != nil { + t.Fatal(err) + } else if c.Files != 2 { + t.Log(c) + t.Error("expected count to be two") + } +} + +func TestDropDevice(t *testing.T) { + db, err := OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := db.Close(); err != nil { + t.Fatal(err) + } + }) + + // Some local files + + // Device 1 + err = db.Update("a", protocol.DeviceID{1}, []protocol.FileInfo{ + genFile("test1", 1, 1), + genFile("test2", 2, 2), + }) + if err != nil { + t.Fatal(err) + } + + // Device 2 + err = db.Update("a", protocol.DeviceID{2}, []protocol.FileInfo{ + genFile("test1", 1, 1), + genFile("test2", 2, 2), + }) + if err != nil { + t.Fatal(err) + } + + // Drop 1 + if err := db.DropDevice(protocol.DeviceID{1}); err != nil { + t.Fatal(err) + } + + // Check + if _, ok, err := db.GetDeviceFile("a", protocol.DeviceID{1}, "test1"); err != nil || ok { + t.Log(err, ok) + t.Error("expected to not exist") + } + if c, err := db.CountLocal("a", protocol.DeviceID{1}); err != nil { + t.Fatal(err) + } else if c.Files != 0 { + t.Log(c) + t.Error("expected count to be zero") + } + if _, ok, err := db.GetDeviceFile("a", protocol.DeviceID{2}, "test1"); err != nil || !ok { + t.Log(err, ok) + t.Error("expected to exist") + } + if c, err := db.CountLocal("a", protocol.DeviceID{2}); err != nil { + t.Fatal(err) + } else if c.Files != 2 { + t.Log(c) + t.Error("expected count to be two") + } + + // Drop something that doesn't exist + if err := db.DropDevice(protocol.DeviceID{99}); err != nil { + t.Fatal(err) + } +} + +func TestDropAllFiles(t *testing.T) { + db, err := OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := db.Close(); err != nil { + t.Fatal(err) + } + }) + + // Some local files + + // Device 1 folder A + err = db.Update("a", protocol.DeviceID{1}, []protocol.FileInfo{ + genFile("test1", 1, 1), + genFile("test2", 2, 2), + }) + if err != nil { + t.Fatal(err) + } + + // Device 1 folder B + err = db.Update("b", protocol.DeviceID{1}, []protocol.FileInfo{ + genFile("test1", 1, 1), + genFile("test2", 2, 2), + }) + if err != nil { + t.Fatal(err) + } + + // Drop folder A + if err := db.DropAllFiles("a", protocol.DeviceID{1}); err != nil { + t.Fatal(err) + } + + // Check + if _, ok, err := db.GetDeviceFile("a", protocol.DeviceID{1}, "test1"); err != nil || ok { + t.Log(err, ok) + t.Error("expected to not exist") + } + if c, err := db.CountLocal("a", protocol.DeviceID{1}); err != nil { + t.Fatal(err) + } else if c.Files != 0 { + t.Log(c) + t.Error("expected count to be zero") + } + if _, ok, err := db.GetDeviceFile("b", protocol.DeviceID{1}, "test1"); err != nil || !ok { + t.Log(err, ok) + t.Error("expected to exist") + } + if c, err := db.CountLocal("b", protocol.DeviceID{1}); err != nil { + t.Fatal(err) + } else if c.Files != 2 { + t.Log(c) + t.Error("expected count to be two") + } + + // Drop things that don't exist + if err := db.DropAllFiles("a", protocol.DeviceID{99}); err != nil { + t.Fatal(err) + } + if err := db.DropAllFiles("trolol", protocol.DeviceID{1}); err != nil { + t.Fatal(err) + } + if err := db.DropAllFiles("trolol", protocol.DeviceID{99}); err != nil { + t.Fatal(err) + } +} + +func TestConcurrentUpdate(t *testing.T) { + t.Parallel() + + db, err := Open(filepath.Join(t.TempDir(), "db")) + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := db.Close(); err != nil { + t.Fatal(err) + } + }) + + const folderID = "test" + + files := []protocol.FileInfo{ + genFile("test1", 1, 1), + genFile("test2", 2, 2), + genFile("test3", 3, 3), + genFile("test4", 4, 4), + } + + const n = 32 + res := make([]error, n) + var wg sync.WaitGroup + wg.Add(n) + for i := range n { + go func() { + res[i] = db.Update(folderID, protocol.DeviceID{byte(i), byte(i), byte(i)}, files) + wg.Done() + }() + } + wg.Wait() + for i, err := range res { + if err != nil { + t.Errorf("%d: %v", i, err) + } + } +} + +func TestConcurrentUpdateSelect(t *testing.T) { + t.Parallel() + + db, err := Open(filepath.Join(t.TempDir(), "db")) + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := db.Close(); err != nil { + t.Fatal(err) + } + }) + + const folderID = "test" + + // Some local files + files := []protocol.FileInfo{ + genFile("test1", 1, 1), + genFile("test2", 2, 2), + genFile("test3", 3, 3), + genFile("test4", 4, 4), + } + + // Insert the files for a remote device + if err := db.Update(folderID, protocol.DeviceID{42}, files); err != nil { + t.Fatal() + } + + // Iterate over handled files and insert them for the local device. + // This is similar to a pattern we have in other places and should + // work. + handled := 0 + it, errFn := db.AllNeededGlobalFiles(folderID, protocol.LocalDeviceID, config.PullOrderAlphabetic, 0, 0) + for glob := range it { + glob.Version = glob.Version.Update(1) + if err := db.Update(folderID, protocol.LocalDeviceID, []protocol.FileInfo{glob}); err != nil { + t.Fatal(err) + } + handled++ + } + if err := errFn(); err != nil { + t.Fatal(err) + } + + if handled != len(files) { + t.Log(handled) + t.Error("should have handled all the files") + } +} + +func TestAllForBlocksHash(t *testing.T) { + t.Parallel() + + sdb, err := OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := sdb.Close(); err != nil { + t.Fatal(err) + } + }) + + // test1 is unique, while test2 and test3 have the same blocks and hence + // the same blocks hash + + files := []protocol.FileInfo{ + genFile("test1", 1, 1), + genFile("test2", 2, 2), + genFile("test3", 3, 3), + } + files[2].Blocks = files[1].Blocks + + if err := sdb.Update(folderID, protocol.LocalDeviceID, files); err != nil { + t.Fatal(err) + } + + // Check test1 + + test1, ok, err := sdb.GetDeviceFile(folderID, protocol.LocalDeviceID, "test1") + if err != nil || !ok { + t.Fatal("expected to exist") + } + + vals := mustCollect[db.FileMetadata](t)(sdb.AllLocalFilesWithBlocksHash(folderID, test1.BlocksHash)) + if len(vals) != 1 { + t.Log(vals) + t.Fatal("expected one file to match") + } + + // Check test2 which also matches test3 + + test2, ok, err := sdb.GetDeviceFile(folderID, protocol.LocalDeviceID, "test2") + if err != nil || !ok { + t.Fatal("expected to exist") + } + + vals = mustCollect[db.FileMetadata](t)(sdb.AllLocalFilesWithBlocksHash(folderID, test2.BlocksHash)) + if len(vals) != 2 { + t.Log(vals) + t.Fatal("expected two files to match") + } + if vals[0].Name != "test2" { + t.Log(vals[0]) + t.Error("expected test2") + } + if vals[1].Name != "test3" { + t.Log(vals[1]) + t.Error("expected test3") + } +} + +func TestBlocklistGarbageCollection(t *testing.T) { + t.Parallel() + + sdb, err := OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := sdb.Close(); err != nil { + t.Fatal(err) + } + }) + svc := sdb.Service(time.Hour).(*Service) + + // Add three files + + files := []protocol.FileInfo{ + genFile("test1", 1, 1), + genFile("test2", 2, 2), + genFile("test3", 3, 3), + } + + if err := sdb.Update(folderID, protocol.LocalDeviceID, files); err != nil { + t.Fatal(err) + } + + // There should exist three blockslists and six blocks + + fdb, err := sdb.getFolderDB(folderID, false) + if err != nil { + t.Fatal(err) + } + + var count int + if err := fdb.sql.Get(&count, `SELECT count(*) FROM blocklists`); err != nil { + t.Fatal(err) + } + if count != 3 { + t.Log(count) + t.Fatal("expected 3 blocklists") + } + if err := fdb.sql.Get(&count, `SELECT count(*) FROM blocks`); err != nil { + t.Fatal(err) + } + if count != 6 { + t.Log(count) + t.Fatal("expected 6 blocks") + } + + // Mark test3 as deleted, it's blocks and blocklist are now eligible for collection + files = files[2:] + files[0].SetDeleted(42) + if err := sdb.Update(folderID, protocol.LocalDeviceID, files); err != nil { + t.Fatal(err) + } + + // Run garbage collection + if err := svc.periodic(context.Background()); err != nil { + t.Fatal(err) + } + + // There should exist two blockslists and four blocks + + if err := fdb.sql.Get(&count, `SELECT count(*) FROM blocklists`); err != nil { + t.Fatal(err) + } + if count != 2 { + t.Log(count) + t.Error("expected 2 blocklists") + } + if err := fdb.sql.Get(&count, `SELECT count(*) FROM blocks`); err != nil { + t.Fatal(err) + } + if count != 3 { + t.Log(count) + t.Error("expected 3 blocks") + } +} + +func TestInsertLargeFile(t *testing.T) { + t.Parallel() + + sdb, err := OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := sdb.Close(); err != nil { + t.Fatal(err) + } + }) + + // Add a large file (many blocks) + + files := []protocol.FileInfo{genFile("test1", 16000, 1)} + if err := sdb.Update(folderID, protocol.LocalDeviceID, files); err != nil { + t.Fatal(err) + } + + // Verify all the blocks are here + + for i, block := range files[0].Blocks { + bs, err := itererr.Collect(sdb.AllLocalBlocksWithHash(folderID, block.Hash)) + if err != nil { + t.Fatal(err) + } + if len(bs) == 0 { + t.Error("missing blocks for", i) + } + } +} + +func TestErrorWrap(t *testing.T) { + if wrap(nil, "foo") != nil { + t.Fatal("nil should wrap to nil") + } + + fooErr := errors.New("foo") + if err := wrap(fooErr); err.Error() != "testerrorwrap: foo" { + t.Fatalf("%q", err) + } + + if err := wrap(fooErr, "bar", "baz"); err.Error() != "testerrorwrap (bar, baz): foo" { + t.Fatalf("%q", err) + } +} + +func TestStrangeDeletedGlobalBug(t *testing.T) { + // This exercises an edge case with serialisation and ordering of + // version vectors. It does not need to make sense, it just needs to + // pass. + + t.Parallel() + + sdb, err := OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := sdb.Close(); err != nil { + t.Fatal(err) + } + }) + + // One remote device announces the original version of the file + + file := genFile("test", 1, 1) + file.Version = protocol.Vector{Counters: []protocol.Counter{{ID: 35494436325452, Value: 1742900373}}} + t.Log("orig", file.Version) + sdb.Update(folderID, protocol.DeviceID{42}, []protocol.FileInfo{file}) + + // Another one announces a newer one that is deleted + + del := file + del.SetDeleted(43) + del.Version = protocol.Vector{Counters: []protocol.Counter{{ID: 55445057455644, Value: 1742918457}, {ID: 35494436325452, Value: 1742900373}}} + t.Log("del", del.Version) + sdb.Update(folderID, protocol.DeviceID{43}, []protocol.FileInfo{del}) + + // We have an instance of the original file + + sdb.Update(folderID, protocol.LocalDeviceID, []protocol.FileInfo{file}) + + // Which one is the global? It should be the deleted one, clearly. + + g, _, err := sdb.GetGlobalFile(folderID, "test") + if err != nil { + t.Fatal(err) + } + if !g.Deleted { + t.Log(g) + t.Fatal("should be deleted") + } +} + +func mustCollect[T any](t *testing.T) func(it iter.Seq[T], errFn func() error) []T { + t.Helper() + return func(it iter.Seq[T], errFn func() error) []T { + t.Helper() + vals, err := itererr.Collect(it, errFn) + if err != nil { + t.Fatal(err) + } + return vals + } +} + +func fiNames(fs []protocol.FileInfo) []string { + names := make([]string, len(fs)) + for i, fi := range fs { + names[i] = fi.Name + } + return names +} + +func genDir(name string, seq int) protocol.FileInfo { + return protocol.FileInfo{ + Name: name, + Type: protocol.FileInfoTypeDirectory, + ModifiedS: time.Now().Unix(), + ModifiedBy: 1, + Sequence: int64(seq), + Version: protocol.Vector{}.Update(1), + Permissions: 0o755, + ModifiedNs: 12345678, + } +} + +func genFile(name string, numBlocks int, seq int) protocol.FileInfo { + ts := timeutil.StrictlyMonotonicNanos() + s := ts / 1e9 + ns := int32(ts % 1e9) + return protocol.FileInfo{ + Name: name, + Size: int64(numBlocks) * blockSize, + ModifiedS: s, + ModifiedBy: 1, + Version: protocol.Vector{}.Update(1), + Sequence: int64(seq), + Blocks: genBlocks(name, 0, numBlocks), + Permissions: 0o644, + ModifiedNs: ns, + RawBlockSize: blockSize, + } +} + +func genBlocks(name string, seed, count int) []protocol.BlockInfo { + b := make([]protocol.BlockInfo, count) + for i := range b { + b[i].Hash = genBlockHash(name, seed, i) + b[i].Size = blockSize + b[i].Offset = (blockSize) * int64(i) + } + return b +} + +func genBlockHash(name string, seed, index int) []byte { + bs := sha256.Sum256([]byte(name)) + ebs := binary.LittleEndian.AppendUint64(nil, uint64(seed)) + for i := range ebs { + bs[i] ^= ebs[i] + } + ebs = binary.LittleEndian.AppendUint64(nil, uint64(index)) + for i := range ebs { + bs[i] ^= ebs[i] + } + return bs[:] +} diff --git a/internal/db/sqlite/db_update.go b/internal/db/sqlite/db_update.go new file mode 100644 index 000000000..df4e82892 --- /dev/null +++ b/internal/db/sqlite/db_update.go @@ -0,0 +1,70 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package sqlite + +import ( + "fmt" + "os" + "runtime" + "strings" +) + +func (s *DB) DropFolder(folder string) error { + s.folderDBsMut.Lock() + defer s.folderDBsMut.Unlock() + s.updateLock.Lock() + defer s.updateLock.Unlock() + _, err := s.stmt(` + DELETE FROM folders + WHERE folder_id = ? + `).Exec(folder) + if fdb, ok := s.folderDBs[folder]; ok { + fdb.Close() + _ = os.Remove(fdb.path) + _ = os.Remove(fdb.path + "-wal") + _ = os.Remove(fdb.path + "-shm") + delete(s.folderDBs, folder) + } + return wrap(err) +} + +func (s *DB) ListFolders() ([]string, error) { + var res []string + err := s.stmt(` + SELECT folder_id FROM folders + ORDER BY folder_id + `).Select(&res) + return res, wrap(err) +} + +// wrap returns the error wrapped with the calling function name and +// optional extra context strings as prefix. A nil error wraps to nil. +func wrap(err error, context ...string) error { + if err == nil { + return nil + } + + prefix := "error" + pc, _, _, ok := runtime.Caller(1) + details := runtime.FuncForPC(pc) + if ok && details != nil { + prefix = strings.ToLower(details.Name()) + if dotIdx := strings.LastIndex(prefix, "."); dotIdx > 0 { + prefix = prefix[dotIdx+1:] + } + } + + if len(context) > 0 { + for i := range context { + context[i] = strings.TrimSpace(context[i]) + } + extra := strings.Join(context, ", ") + return fmt.Errorf("%s (%s): %w", prefix, extra, err) + } + + return fmt.Errorf("%s: %w", prefix, err) +} diff --git a/lib/db/debug.go b/internal/db/sqlite/debug.go similarity index 58% rename from lib/db/debug.go rename to internal/db/sqlite/debug.go index 26abbaf01..acc711a2e 100644 --- a/lib/db/debug.go +++ b/internal/db/sqlite/debug.go @@ -1,17 +1,15 @@ -// Copyright (C) 2014 The Syncthing Authors. +// Copyright (C) 2025 The Syncthing Authors. // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at https://mozilla.org/MPL/2.0/. -package db +package sqlite import ( "github.com/syncthing/syncthing/lib/logger" ) -var l = logger.DefaultLogger.NewFacility("db", "The database layer") +var l = logger.DefaultLogger.NewFacility("sqlite", "SQLite database") -func shouldDebug() bool { - return l.ShouldDebug("db") -} +func shouldDebug() bool { return l.ShouldDebug("sqlite") } diff --git a/internal/db/sqlite/folderdb_counts.go b/internal/db/sqlite/folderdb_counts.go new file mode 100644 index 000000000..1c6dfd56f --- /dev/null +++ b/internal/db/sqlite/folderdb_counts.go @@ -0,0 +1,131 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package sqlite + +import ( + "github.com/syncthing/syncthing/internal/db" + "github.com/syncthing/syncthing/lib/protocol" +) + +type countsRow struct { + Type protocol.FileInfoType + Count int + Size int64 + Deleted bool + LocalFlags int64 `db:"local_flags"` +} + +func (s *folderDB) CountLocal(device protocol.DeviceID) (db.Counts, error) { + var res []countsRow + if err := s.stmt(` + SELECT s.type, s.count, s.size, s.local_flags, s.deleted FROM counts s + INNER JOIN devices d ON d.idx = s.device_idx + WHERE d.device_id = ? AND s.local_flags & {{.FlagLocalIgnored}} = 0 + `).Select(&res, device.String()); err != nil { + return db.Counts{}, wrap(err) + } + return summarizeCounts(res), nil +} + +func (s *folderDB) CountNeed(device protocol.DeviceID) (db.Counts, error) { + if device == protocol.LocalDeviceID { + return s.needSizeLocal() + } + return s.needSizeRemote(device) +} + +func (s *folderDB) CountGlobal() (db.Counts, error) { + // Exclude ignored and receive-only changed files from the global count + // (legacy expectation? it's a bit weird since those files can in fact + // be global and you can get them with GetGlobal etc.) + var res []countsRow + err := s.stmt(` + SELECT s.type, s.count, s.size, s.local_flags, s.deleted FROM counts s + WHERE s.local_flags & {{.FlagLocalGlobal}} != 0 AND s.local_flags & {{or .FlagLocalReceiveOnly .FlagLocalIgnored}} = 0 + `).Select(&res) + if err != nil { + return db.Counts{}, wrap(err) + } + return summarizeCounts(res), nil +} + +func (s *folderDB) CountReceiveOnlyChanged() (db.Counts, error) { + var res []countsRow + err := s.stmt(` + SELECT s.type, s.count, s.size, s.local_flags, s.deleted FROM counts s + WHERE local_flags & {{.FlagLocalReceiveOnly}} != 0 + `).Select(&res) + if err != nil { + return db.Counts{}, wrap(err) + } + return summarizeCounts(res), nil +} + +func (s *folderDB) needSizeLocal() (db.Counts, error) { + // The need size for the local device is the sum of entries with the + // need bit set. + var res []countsRow + err := s.stmt(` + SELECT s.type, s.count, s.size, s.local_flags, s.deleted FROM counts s + WHERE s.local_flags & {{.FlagLocalNeeded}} != 0 + `).Select(&res) + if err != nil { + return db.Counts{}, wrap(err) + } + return summarizeCounts(res), nil +} + +func (s *folderDB) needSizeRemote(device protocol.DeviceID) (db.Counts, error) { + var res []countsRow + // See neededGlobalFilesRemote for commentary as that is the same query without summing + if err := s.stmt(` + SELECT g.type, count(*) as count, sum(g.size) as size, g.local_flags, g.deleted FROM files g + WHERE g.local_flags & {{.FlagLocalGlobal}} != 0 AND NOT g.deleted AND NOT g.invalid AND NOT EXISTS ( + SELECT 1 FROM FILES f + INNER JOIN devices d ON d.idx = f.device_idx + WHERE f.name = g.name AND f.version = g.version AND d.device_id = ? + ) + GROUP BY g.type, g.local_flags, g.deleted + + UNION ALL + + SELECT g.type, count(*) as count, sum(g.size) as size, g.local_flags, g.deleted FROM files g + WHERE g.local_flags & {{.FlagLocalGlobal}} != 0 AND g.deleted AND NOT g.invalid AND EXISTS ( + SELECT 1 FROM FILES f + INNER JOIN devices d ON d.idx = f.device_idx + WHERE f.name = g.name AND d.device_id = ? AND NOT f.deleted AND NOT f.invalid + ) + GROUP BY g.type, g.local_flags, g.deleted + `).Select(&res, device.String(), + device.String()); err != nil { + return db.Counts{}, wrap(err) + } + + return summarizeCounts(res), nil +} + +func summarizeCounts(res []countsRow) db.Counts { + c := db.Counts{ + DeviceID: protocol.LocalDeviceID, + } + for _, r := range res { + switch { + case r.Deleted: + c.Deleted += r.Count + case r.Type == protocol.FileInfoTypeFile: + c.Files += r.Count + c.Bytes += r.Size + case r.Type == protocol.FileInfoTypeDirectory: + c.Directories += r.Count + c.Bytes += r.Size + case r.Type == protocol.FileInfoTypeSymlink: + c.Symlinks += r.Count + c.Bytes += r.Size + } + } + return c +} diff --git a/internal/db/sqlite/folderdb_global.go b/internal/db/sqlite/folderdb_global.go new file mode 100644 index 000000000..5dd6c42a3 --- /dev/null +++ b/internal/db/sqlite/folderdb_global.go @@ -0,0 +1,182 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package sqlite + +import ( + "database/sql" + "errors" + "fmt" + "iter" + + "github.com/syncthing/syncthing/internal/db" + "github.com/syncthing/syncthing/internal/itererr" + "github.com/syncthing/syncthing/lib/config" + "github.com/syncthing/syncthing/lib/osutil" + "github.com/syncthing/syncthing/lib/protocol" +) + +func (s *folderDB) GetGlobalFile(file string) (protocol.FileInfo, bool, error) { + file = osutil.NormalizedFilename(file) + + var ind indirectFI + err := s.stmt(` + SELECT fi.fiprotobuf, bl.blprotobuf FROM fileinfos fi + INNER JOIN files f on fi.sequence = f.sequence + LEFT JOIN blocklists bl ON bl.blocklist_hash = f.blocklist_hash + WHERE f.name = ? AND f.local_flags & {{.FlagLocalGlobal}} != 0 + `).Get(&ind, file) + if errors.Is(err, sql.ErrNoRows) { + return protocol.FileInfo{}, false, nil + } + if err != nil { + return protocol.FileInfo{}, false, wrap(err) + } + fi, err := ind.FileInfo() + if err != nil { + return protocol.FileInfo{}, false, wrap(err) + } + return fi, true, nil +} + +func (s *folderDB) GetGlobalAvailability(file string) ([]protocol.DeviceID, error) { + file = osutil.NormalizedFilename(file) + + var devStrs []string + err := s.stmt(` + SELECT d.device_id FROM files f + INNER JOIN devices d ON d.idx = f.device_idx + INNER JOIN files g ON g.version = f.version AND g.name = f.name + WHERE g.name = ? AND g.local_flags & {{.FlagLocalGlobal}} != 0 AND f.device_idx != {{.LocalDeviceIdx}} + ORDER BY d.device_id + `).Select(&devStrs, file) + if errors.Is(err, sql.ErrNoRows) { + return nil, nil + } + if err != nil { + return nil, wrap(err) + } + + devs := make([]protocol.DeviceID, 0, len(devStrs)) + for _, s := range devStrs { + d, err := protocol.DeviceIDFromString(s) + if err != nil { + return nil, wrap(err) + } + devs = append(devs, d) + } + + return devs, nil +} + +func (s *folderDB) AllGlobalFiles() (iter.Seq[db.FileMetadata], func() error) { + it, errFn := iterStructs[db.FileMetadata](s.stmt(` + SELECT f.sequence, f.name, f.type, f.modified as modnanos, f.size, f.deleted, f.invalid, f.local_flags as localflags FROM files f + WHERE f.local_flags & {{.FlagLocalGlobal}} != 0 + ORDER BY f.name + `).Queryx()) + return itererr.Map(it, errFn, func(m db.FileMetadata) (db.FileMetadata, error) { + m.Name = osutil.NativeFilename(m.Name) + return m, nil + }) +} + +func (s *folderDB) AllGlobalFilesPrefix(prefix string) (iter.Seq[db.FileMetadata], func() error) { + if prefix == "" { + return s.AllGlobalFiles() + } + + prefix = osutil.NormalizedFilename(prefix) + end := prefixEnd(prefix) + + it, errFn := iterStructs[db.FileMetadata](s.stmt(` + SELECT f.sequence, f.name, f.type, f.modified as modnanos, f.size, f.deleted, f.invalid, f.local_flags as localflags FROM files f + WHERE f.name >= ? AND f.name < ? AND f.local_flags & {{.FlagLocalGlobal}} != 0 + ORDER BY f.name + `).Queryx(prefix, end)) + return itererr.Map(it, errFn, func(m db.FileMetadata) (db.FileMetadata, error) { + m.Name = osutil.NativeFilename(m.Name) + return m, nil + }) +} + +func (s *folderDB) AllNeededGlobalFiles(device protocol.DeviceID, order config.PullOrder, limit, offset int) (iter.Seq[protocol.FileInfo], func() error) { + var selectOpts string + switch order { + case config.PullOrderRandom: + selectOpts = "ORDER BY RANDOM()" + case config.PullOrderAlphabetic: + selectOpts = "ORDER BY g.name ASC" + case config.PullOrderSmallestFirst: + selectOpts = "ORDER BY g.size ASC" + case config.PullOrderLargestFirst: + selectOpts = "ORDER BY g.size DESC" + case config.PullOrderOldestFirst: + selectOpts = "ORDER BY g.modified ASC" + case config.PullOrderNewestFirst: + selectOpts = "ORDER BY g.modified DESC" + } + + if limit > 0 { + selectOpts += fmt.Sprintf(" LIMIT %d", limit) + } + if offset > 0 { + selectOpts += fmt.Sprintf(" OFFSET %d", offset) + } + + if device == protocol.LocalDeviceID { + return s.neededGlobalFilesLocal(selectOpts) + } + + return s.neededGlobalFilesRemote(device, selectOpts) +} + +func (s *folderDB) neededGlobalFilesLocal(selectOpts string) (iter.Seq[protocol.FileInfo], func() error) { + // Select all the non-ignored files with the need bit set. + it, errFn := iterStructs[indirectFI](s.stmt(` + SELECT fi.fiprotobuf, bl.blprotobuf, g.name, g.size, g.modified FROM fileinfos fi + INNER JOIN files g on fi.sequence = g.sequence + LEFT JOIN blocklists bl ON bl.blocklist_hash = g.blocklist_hash + WHERE g.local_flags & {{.FlagLocalIgnored}} = 0 AND g.local_flags & {{.FlagLocalNeeded}} != 0 + ` + selectOpts).Queryx()) + return itererr.Map(it, errFn, indirectFI.FileInfo) +} + +func (s *folderDB) neededGlobalFilesRemote(device protocol.DeviceID, selectOpts string) (iter.Seq[protocol.FileInfo], func() error) { + // Select: + // + // - all the valid, non-deleted global files that don't have a + // corresponding remote file with the same version. + // + // - all the valid, deleted global files that have a corresponding + // non-deleted and valid remote file (of any version) + + it, errFn := iterStructs[indirectFI](s.stmt(` + SELECT fi.fiprotobuf, bl.blprotobuf, g.name, g.size, g.modified FROM fileinfos fi + INNER JOIN files g on fi.sequence = g.sequence + LEFT JOIN blocklists bl ON bl.blocklist_hash = g.blocklist_hash + WHERE g.local_flags & {{.FlagLocalGlobal}} != 0 AND NOT g.deleted AND NOT g.invalid AND NOT EXISTS ( + SELECT 1 FROM FILES f + INNER JOIN devices d ON d.idx = f.device_idx + WHERE f.name = g.name AND f.version = g.version AND d.device_id = ? + ) + + UNION ALL + + SELECT fi.fiprotobuf, bl.blprotobuf, g.name, g.size, g.modified FROM fileinfos fi + INNER JOIN files g on fi.sequence = g.sequence + LEFT JOIN blocklists bl ON bl.blocklist_hash = g.blocklist_hash + WHERE g.local_flags & {{.FlagLocalGlobal}} != 0 AND g.deleted AND NOT g.invalid AND EXISTS ( + SELECT 1 FROM FILES f + INNER JOIN devices d ON d.idx = f.device_idx + WHERE f.name = g.name AND d.device_id = ? AND NOT f.deleted AND NOT f.invalid + ) + `+selectOpts).Queryx( + device.String(), + device.String(), + )) + return itererr.Map(it, errFn, indirectFI.FileInfo) +} diff --git a/internal/db/sqlite/folderdb_indexid.go b/internal/db/sqlite/folderdb_indexid.go new file mode 100644 index 000000000..1a4f693f5 --- /dev/null +++ b/internal/db/sqlite/folderdb_indexid.go @@ -0,0 +1,151 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package sqlite + +import ( + "database/sql" + "encoding/hex" + "errors" + "fmt" + + "github.com/syncthing/syncthing/internal/itererr" + "github.com/syncthing/syncthing/lib/protocol" +) + +func (s *folderDB) GetIndexID(device protocol.DeviceID) (protocol.IndexID, error) { + // Try a fast read-only query to begin with. If it does not find the ID + // we'll do the full thing under a lock. + var indexID string + if err := s.stmt(` + SELECT i.index_id FROM indexids i + INNER JOIN devices d ON d.idx = i.device_idx + WHERE d.device_id = ? + `).Get(&indexID, device.String()); err == nil && indexID != "" { + idx, err := indexIDFromHex(indexID) + return idx, wrap(err, "select") + } + if device != protocol.LocalDeviceID { + // For non-local devices we do not create the index ID, so return + // zero anyway if we don't have one. + return 0, nil + } + + s.updateLock.Lock() + defer s.updateLock.Unlock() + + // We are now operating only for the local device ID + + if err := s.stmt(` + SELECT index_id FROM indexids WHERE device_idx = {{.LocalDeviceIdx}} + `).Get(&indexID); err != nil && !errors.Is(err, sql.ErrNoRows) { + return 0, wrap(err, "select local") + } + + if indexID == "" { + // Generate a new index ID. Some trickiness in the query as we need + // to find the max sequence of local files if there already exist + // any. + id := protocol.NewIndexID() + if _, err := s.stmt(` + INSERT INTO indexids (device_idx, index_id, sequence) + SELECT {{.LocalDeviceIdx}}, ?, COALESCE(MAX(sequence), 0) FROM files + WHERE device_idx = {{.LocalDeviceIdx}} + ON CONFLICT DO UPDATE SET index_id = ? + `).Exec(indexIDToHex(id), indexIDToHex(id)); err != nil { + return 0, wrap(err, "insert") + } + return id, nil + } + + return indexIDFromHex(indexID) +} + +func (s *folderDB) SetIndexID(device protocol.DeviceID, id protocol.IndexID) error { + s.updateLock.Lock() + defer s.updateLock.Unlock() + + deviceIdx, err := s.deviceIdxLocked(device) + if err != nil { + return wrap(err, "device idx") + } + + if _, err := s.stmt(` + INSERT OR REPLACE INTO indexids (device_idx, index_id, sequence) values (?, ?, 0) + `).Exec(deviceIdx, indexIDToHex(id)); err != nil { + return wrap(err, "insert") + } + return nil +} + +func (s *folderDB) DropAllIndexIDs() error { + s.updateLock.Lock() + defer s.updateLock.Unlock() + _, err := s.stmt(`DELETE FROM indexids`).Exec() + return wrap(err) +} + +func (s *folderDB) GetDeviceSequence(device protocol.DeviceID) (int64, error) { + var res sql.NullInt64 + err := s.stmt(` + SELECT sequence FROM indexids i + INNER JOIN devices d ON d.idx = i.device_idx + WHERE d.device_id = ? + `).Get(&res, device.String()) + if errors.Is(err, sql.ErrNoRows) { + return 0, nil + } + if err != nil { + return 0, wrap(err) + } + if !res.Valid { + return 0, nil + } + return res.Int64, nil +} + +func (s *folderDB) RemoteSequences() (map[protocol.DeviceID]int64, error) { + type row struct { + Device string + Seq int64 + } + + it, errFn := iterStructs[row](s.stmt(` + SELECT d.device_id AS device, i.sequence AS seq FROM indexids i + INNER JOIN devices d ON d.idx = i.device_idx + WHERE i.device_idx != {{.LocalDeviceIdx}} + `).Queryx()) + + res := make(map[protocol.DeviceID]int64) + for row, err := range itererr.Zip(it, errFn) { + if err != nil { + return nil, wrap(err) + } + dev, err := protocol.DeviceIDFromString(row.Device) + if err != nil { + return nil, wrap(err, "device ID") + } + res[dev] = row.Seq + } + return res, nil +} + +func indexIDFromHex(s string) (protocol.IndexID, error) { + bs, err := hex.DecodeString(s) + if err != nil { + return 0, fmt.Errorf("indexIDFromHex: %q: %w", s, err) + } + var id protocol.IndexID + if err := id.Unmarshal(bs); err != nil { + return 0, fmt.Errorf("indexIDFromHex: %q: %w", s, err) + } + return id, nil +} + +func indexIDToHex(i protocol.IndexID) string { + bs, _ := i.Marshal() + return hex.EncodeToString(bs) +} diff --git a/internal/db/sqlite/folderdb_local.go b/internal/db/sqlite/folderdb_local.go new file mode 100644 index 000000000..563174a73 --- /dev/null +++ b/internal/db/sqlite/folderdb_local.go @@ -0,0 +1,128 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package sqlite + +import ( + "database/sql" + "errors" + "fmt" + "iter" + + "github.com/syncthing/syncthing/internal/db" + "github.com/syncthing/syncthing/internal/itererr" + "github.com/syncthing/syncthing/lib/osutil" + "github.com/syncthing/syncthing/lib/protocol" +) + +func (s *folderDB) GetDeviceFile(device protocol.DeviceID, file string) (protocol.FileInfo, bool, error) { + file = osutil.NormalizedFilename(file) + + var ind indirectFI + err := s.stmt(` + SELECT fi.fiprotobuf, bl.blprotobuf FROM fileinfos fi + INNER JOIN files f on fi.sequence = f.sequence + LEFT JOIN blocklists bl ON bl.blocklist_hash = f.blocklist_hash + INNER JOIN devices d ON f.device_idx = d.idx + WHERE d.device_id = ? AND f.name = ? + `).Get(&ind, device.String(), file) + if errors.Is(err, sql.ErrNoRows) { + return protocol.FileInfo{}, false, nil + } + if err != nil { + return protocol.FileInfo{}, false, wrap(err) + } + fi, err := ind.FileInfo() + if err != nil { + return protocol.FileInfo{}, false, wrap(err, "indirect") + } + return fi, true, nil +} + +func (s *folderDB) AllLocalFiles(device protocol.DeviceID) (iter.Seq[protocol.FileInfo], func() error) { + it, errFn := iterStructs[indirectFI](s.stmt(` + SELECT fi.fiprotobuf, bl.blprotobuf FROM fileinfos fi + INNER JOIN files f on fi.sequence = f.sequence + LEFT JOIN blocklists bl ON bl.blocklist_hash = f.blocklist_hash + INNER JOIN devices d ON d.idx = f.device_idx + WHERE d.device_id = ? + `).Queryx(device.String())) + return itererr.Map(it, errFn, indirectFI.FileInfo) +} + +func (s *folderDB) AllLocalFilesBySequence(device protocol.DeviceID, startSeq int64, limit int) (iter.Seq[protocol.FileInfo], func() error) { + var limitStr string + if limit > 0 { + limitStr = fmt.Sprintf(" LIMIT %d", limit) + } + it, errFn := iterStructs[indirectFI](s.stmt(` + SELECT fi.fiprotobuf, bl.blprotobuf FROM fileinfos fi + INNER JOIN files f on fi.sequence = f.sequence + LEFT JOIN blocklists bl ON bl.blocklist_hash = f.blocklist_hash + INNER JOIN devices d ON d.idx = f.device_idx + WHERE d.device_id = ? AND f.sequence >= ? + ORDER BY f.sequence`+limitStr).Queryx( + device.String(), startSeq)) + return itererr.Map(it, errFn, indirectFI.FileInfo) +} + +func (s *folderDB) AllLocalFilesWithPrefix(device protocol.DeviceID, prefix string) (iter.Seq[protocol.FileInfo], func() error) { + if prefix == "" { + return s.AllLocalFiles(device) + } + + prefix = osutil.NormalizedFilename(prefix) + end := prefixEnd(prefix) + + it, errFn := iterStructs[indirectFI](s.sql.Queryx(` + SELECT fi.fiprotobuf, bl.blprotobuf FROM fileinfos fi + INNER JOIN files f on fi.sequence = f.sequence + LEFT JOIN blocklists bl ON bl.blocklist_hash = f.blocklist_hash + INNER JOIN devices d ON d.idx = f.device_idx + WHERE d.device_id = ? AND f.name >= ? AND f.name < ? + `, device.String(), prefix, end)) + return itererr.Map(it, errFn, indirectFI.FileInfo) +} + +func (s *folderDB) AllLocalFilesWithBlocksHash(h []byte) (iter.Seq[db.FileMetadata], func() error) { + return iterStructs[db.FileMetadata](s.stmt(` + SELECT f.sequence, f.name, f.type, f.modified as modnanos, f.size, f.deleted, f.invalid, f.local_flags as localflags FROM files f + WHERE f.device_idx = {{.LocalDeviceIdx}} AND f.blocklist_hash = ? + `).Queryx(h)) +} + +func (s *folderDB) AllLocalBlocksWithHash(hash []byte) (iter.Seq[db.BlockMapEntry], func() error) { + // We involve the files table in this select because deletion of blocks + // & blocklists is deferred (garbage collected) while the files list is + // not. This filters out blocks that are in fact deleted. + return iterStructs[db.BlockMapEntry](s.stmt(` + SELECT f.blocklist_hash as blocklisthash, b.idx as blockindex, b.offset, b.size, f.name as filename FROM files f + LEFT JOIN blocks b ON f.blocklist_hash = b.blocklist_hash + WHERE f.device_idx = {{.LocalDeviceIdx}} AND b.hash = ? + `).Queryx(hash)) +} + +func (s *folderDB) ListDevicesForFolder() ([]protocol.DeviceID, error) { + var res []string + err := s.stmt(` + SELECT DISTINCT d.device_id FROM counts s + INNER JOIN devices d ON d.idx = s.device_idx + WHERE s.count > 0 AND s.device_idx != {{.LocalDeviceIdx}} + ORDER BY d.device_id + `).Select(&res) + if err != nil { + return nil, wrap(err) + } + + devs := make([]protocol.DeviceID, len(res)) + for i, s := range res { + devs[i], err = protocol.DeviceIDFromString(s) + if err != nil { + return nil, wrap(err) + } + } + return devs, nil +} diff --git a/internal/db/sqlite/folderdb_mtimes.go b/internal/db/sqlite/folderdb_mtimes.go new file mode 100644 index 000000000..051c933ad --- /dev/null +++ b/internal/db/sqlite/folderdb_mtimes.go @@ -0,0 +1,45 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package sqlite + +import ( + "time" +) + +func (s *folderDB) GetMtime(name string) (ondisk, virtual time.Time) { + var res struct { + Ondisk int64 + Virtual int64 + } + if err := s.stmt(` + SELECT m.ondisk, m.virtual FROM mtimes m + WHERE m.name = ? + `).Get(&res, name); err != nil { + return time.Time{}, time.Time{} + } + return time.Unix(0, res.Ondisk), time.Unix(0, res.Virtual) +} + +func (s *folderDB) PutMtime(name string, ondisk, virtual time.Time) error { + s.updateLock.Lock() + defer s.updateLock.Unlock() + _, err := s.stmt(` + INSERT OR REPLACE INTO mtimes (name, ondisk, virtual) + VALUES (?, ?, ?) + `).Exec(name, ondisk.UnixNano(), virtual.UnixNano()) + return wrap(err) +} + +func (s *folderDB) DeleteMtime(name string) error { + s.updateLock.Lock() + defer s.updateLock.Unlock() + _, err := s.stmt(` + DELETE FROM mtimes + WHERE name = ? + `).Exec(name) + return wrap(err) +} diff --git a/internal/db/sqlite/folderdb_open.go b/internal/db/sqlite/folderdb_open.go new file mode 100644 index 000000000..5c38b112d --- /dev/null +++ b/internal/db/sqlite/folderdb_open.go @@ -0,0 +1,114 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package sqlite + +import ( + "time" + + "github.com/syncthing/syncthing/lib/protocol" +) + +type folderDB struct { + folderID string + *baseDB + + localDeviceIdx int64 + deleteRetention time.Duration +} + +func openFolderDB(folder, path string, deleteRetention time.Duration) (*folderDB, error) { + pragmas := []string{ + "journal_mode = WAL", + "optimize = 0x10002", + "auto_vacuum = INCREMENTAL", + "default_temp_store = MEMORY", + "temp_store = MEMORY", + } + schemas := []string{ + "sql/schema/common/*", + "sql/schema/folder/*", + } + migrations := []string{ + "sql/migrations/common/*", + "sql/migrations/folder/*", + } + + base, err := openBase(path, maxDBConns, pragmas, schemas, migrations) + if err != nil { + return nil, err + } + + fdb := &folderDB{ + folderID: folder, + baseDB: base, + deleteRetention: deleteRetention, + } + + _ = fdb.PutKV("folderID", []byte(folder)) + + // Touch device IDs that should always exist and have a low index + // numbers, and will never change + fdb.localDeviceIdx, _ = fdb.deviceIdxLocked(protocol.LocalDeviceID) + fdb.tplInput["LocalDeviceIdx"] = fdb.localDeviceIdx + + return fdb, nil +} + +// Open the database with options suitable for the migration inserts. This +// is not a safe mode of operation for normal processing, use only for bulk +// inserts with a close afterwards. +func openFolderDBForMigration(folder, path string, deleteRetention time.Duration) (*folderDB, error) { + pragmas := []string{ + "journal_mode = OFF", + "default_temp_store = MEMORY", + "temp_store = MEMORY", + "foreign_keys = 0", + "synchronous = 0", + "locking_mode = EXCLUSIVE", + } + schemas := []string{ + "sql/schema/common/*", + "sql/schema/folder/*", + } + + base, err := openBase(path, 1, pragmas, schemas, nil) + if err != nil { + return nil, err + } + + fdb := &folderDB{ + folderID: folder, + baseDB: base, + deleteRetention: deleteRetention, + } + + // Touch device IDs that should always exist and have a low index + // numbers, and will never change + fdb.localDeviceIdx, _ = fdb.deviceIdxLocked(protocol.LocalDeviceID) + fdb.tplInput["LocalDeviceIdx"] = fdb.localDeviceIdx + + return fdb, nil +} + +func (s *folderDB) deviceIdxLocked(deviceID protocol.DeviceID) (int64, error) { + devStr := deviceID.String() + if _, err := s.stmt(` + INSERT OR IGNORE INTO devices(device_id) + VALUES (?) + `).Exec(devStr); err != nil { + return 0, wrap(err) + } + var idx int64 + if err := s.stmt(` + SELECT idx FROM devices + WHERE device_id = ? + `).Get(&idx, devStr); err != nil { + return 0, wrap(err) + } + + return idx, nil +} diff --git a/internal/db/sqlite/folderdb_update.go b/internal/db/sqlite/folderdb_update.go new file mode 100644 index 000000000..f787ab8c3 --- /dev/null +++ b/internal/db/sqlite/folderdb_update.go @@ -0,0 +1,532 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package sqlite + +import ( + "cmp" + "context" + "fmt" + "slices" + + "github.com/jmoiron/sqlx" + "github.com/syncthing/syncthing/internal/gen/dbproto" + "github.com/syncthing/syncthing/internal/itererr" + "github.com/syncthing/syncthing/lib/osutil" + "github.com/syncthing/syncthing/lib/protocol" + "github.com/syncthing/syncthing/lib/sliceutil" + "google.golang.org/protobuf/proto" +) + +const ( + // Arbitrarily chosen values for checkpoint frequency.... + updatePointsPerFile = 100 + updatePointsPerBlock = 1 + updatePointsThreshold = 250_000 +) + +func (s *folderDB) Update(device protocol.DeviceID, fs []protocol.FileInfo) error { + s.updateLock.Lock() + defer s.updateLock.Unlock() + + deviceIdx, err := s.deviceIdxLocked(device) + if err != nil { + return wrap(err) + } + + tx, err := s.sql.BeginTxx(context.Background(), nil) + if err != nil { + return wrap(err) + } + defer tx.Rollback() //nolint:errcheck + txp := &txPreparedStmts{Tx: tx} + + //nolint:sqlclosecheck + insertFileStmt, err := txp.Preparex(` + INSERT OR REPLACE INTO files (device_idx, remote_sequence, name, type, modified, size, version, deleted, invalid, local_flags, blocklist_hash) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + RETURNING sequence + `) + if err != nil { + return wrap(err, "prepare insert file") + } + + //nolint:sqlclosecheck + insertFileInfoStmt, err := txp.Preparex(` + INSERT INTO fileinfos (sequence, fiprotobuf) + VALUES (?, ?) + `) + if err != nil { + return wrap(err, "prepare insert fileinfo") + } + + //nolint:sqlclosecheck + insertBlockListStmt, err := txp.Preparex(` + INSERT OR IGNORE INTO blocklists (blocklist_hash, blprotobuf) + VALUES (?, ?) + `) + if err != nil { + return wrap(err, "prepare insert blocklist") + } + + var prevRemoteSeq int64 + for i, f := range fs { + f.Name = osutil.NormalizedFilename(f.Name) + + var blockshash *[]byte + if len(f.Blocks) > 0 { + f.BlocksHash = protocol.BlocksHash(f.Blocks) + blockshash = &f.BlocksHash + } else { + f.BlocksHash = nil + } + + if f.Type == protocol.FileInfoTypeDirectory { + f.Size = 128 // synthetic directory size + } + + // Insert the file. + // + // If it is a remote file, set remote_sequence otherwise leave it at + // null. Returns the new local sequence. + var remoteSeq *int64 + if device != protocol.LocalDeviceID { + if i > 0 && f.Sequence == prevRemoteSeq { + return fmt.Errorf("duplicate remote sequence number %d", prevRemoteSeq) + } + prevRemoteSeq = f.Sequence + remoteSeq = &f.Sequence + } + var localSeq int64 + if err := insertFileStmt.Get(&localSeq, deviceIdx, remoteSeq, f.Name, f.Type, f.ModTime().UnixNano(), f.Size, f.Version.String(), f.IsDeleted(), f.IsInvalid(), f.LocalFlags, blockshash); err != nil { + return wrap(err, "insert file") + } + + if len(f.Blocks) > 0 { + // Indirect the block list + blocks := sliceutil.Map(f.Blocks, protocol.BlockInfo.ToWire) + bs, err := proto.Marshal(&dbproto.BlockList{Blocks: blocks}) + if err != nil { + return wrap(err, "marshal blocklist") + } + if _, err := insertBlockListStmt.Exec(f.BlocksHash, bs); err != nil { + return wrap(err, "insert blocklist") + } + + if device == protocol.LocalDeviceID { + // Insert all blocks + if err := s.insertBlocksLocked(txp, f.BlocksHash, f.Blocks); err != nil { + return wrap(err, "insert blocks") + } + } + + f.Blocks = nil + } + + // Insert the fileinfo + if device == protocol.LocalDeviceID { + f.Sequence = localSeq + } + bs, err := proto.Marshal(f.ToWire(true)) + if err != nil { + return wrap(err, "marshal fileinfo") + } + if _, err := insertFileInfoStmt.Exec(localSeq, bs); err != nil { + return wrap(err, "insert fileinfo") + } + + // Update global and need + if err := s.recalcGlobalForFile(txp, f.Name); err != nil { + return wrap(err) + } + } + + if err := tx.Commit(); err != nil { + return wrap(err) + } + + s.periodicCheckpointLocked(fs) + return nil +} + +func (s *folderDB) DropDevice(device protocol.DeviceID) error { + if device == protocol.LocalDeviceID { + panic("bug: cannot drop local device") + } + + s.updateLock.Lock() + defer s.updateLock.Unlock() + + tx, err := s.sql.BeginTxx(context.Background(), nil) + if err != nil { + return wrap(err) + } + defer tx.Rollback() //nolint:errcheck + txp := &txPreparedStmts{Tx: tx} + + // Drop the device, which cascades to delete all files etc for it + if _, err := tx.Exec(`DELETE FROM devices WHERE device_id = ?`, device.String()); err != nil { + return wrap(err) + } + + // Recalc the globals for all affected folders + if err := s.recalcGlobalForFolder(txp); err != nil { + return wrap(err) + } + + return wrap(tx.Commit()) +} + +func (s *folderDB) DropAllFiles(device protocol.DeviceID) error { + s.updateLock.Lock() + defer s.updateLock.Unlock() + + // This is a two part operation, first dropping all the files and then + // recalculating the global state for the entire folder. + + deviceIdx, err := s.deviceIdxLocked(device) + if err != nil { + return wrap(err) + } + + tx, err := s.sql.BeginTxx(context.Background(), nil) + if err != nil { + return wrap(err) + } + defer tx.Rollback() //nolint:errcheck + txp := &txPreparedStmts{Tx: tx} + + // Drop all the file entries + + result, err := tx.Exec(` + DELETE FROM files + WHERE device_idx = ? + `, deviceIdx) + if err != nil { + return wrap(err) + } + if n, err := result.RowsAffected(); err == nil && n == 0 { + // The delete affected no rows, so we don't need to redo the entire + // global/need calculation. + return wrap(tx.Commit()) + } + + // Recalc global for the entire folder + + if err := s.recalcGlobalForFolder(txp); err != nil { + return wrap(err) + } + return wrap(tx.Commit()) +} + +func (s *folderDB) DropFilesNamed(device protocol.DeviceID, names []string) error { + for i := range names { + names[i] = osutil.NormalizedFilename(names[i]) + } + + s.updateLock.Lock() + defer s.updateLock.Unlock() + + deviceIdx, err := s.deviceIdxLocked(device) + if err != nil { + return wrap(err) + } + + tx, err := s.sql.BeginTxx(context.Background(), nil) + if err != nil { + return wrap(err) + } + defer tx.Rollback() //nolint:errcheck + txp := &txPreparedStmts{Tx: tx} + + // Drop the named files + + query, args, err := sqlx.In(` + DELETE FROM files + WHERE device_idx = ? AND name IN (?) + `, deviceIdx, names) + if err != nil { + return wrap(err) + } + if _, err := tx.Exec(query, args...); err != nil { + return wrap(err) + } + + // Recalc globals for the named files + + for _, name := range names { + if err := s.recalcGlobalForFile(txp, name); err != nil { + return wrap(err) + } + } + + return wrap(tx.Commit()) +} + +func (*folderDB) insertBlocksLocked(tx *txPreparedStmts, blocklistHash []byte, blocks []protocol.BlockInfo) error { + if len(blocks) == 0 { + return nil + } + bs := make([]map[string]any, len(blocks)) + for i, b := range blocks { + bs[i] = map[string]any{ + "hash": b.Hash, + "blocklist_hash": blocklistHash, + "idx": i, + "offset": b.Offset, + "size": b.Size, + } + } + + // Very large block lists (>8000 blocks) result in "too many variables" + // error. Chunk it to a reasonable size. + for chunk := range slices.Chunk(bs, 1000) { + if _, err := tx.NamedExec(` + INSERT OR IGNORE INTO blocks (hash, blocklist_hash, idx, offset, size) + VALUES (:hash, :blocklist_hash, :idx, :offset, :size) + `, chunk); err != nil { + return wrap(err) + } + } + return nil +} + +func (s *folderDB) recalcGlobalForFolder(txp *txPreparedStmts) error { + // Select files where there is no global, those are the ones we need to + // recalculate. + //nolint:sqlclosecheck + namesStmt, err := txp.Preparex(` + SELECT f.name FROM files f + WHERE NOT EXISTS ( + SELECT 1 FROM files g + WHERE g.name = f.name AND g.local_flags & ? != 0 + ) + GROUP BY name + `) + if err != nil { + return wrap(err) + } + rows, err := namesStmt.Queryx(protocol.FlagLocalGlobal) + if err != nil { + return wrap(err) + } + defer rows.Close() + for rows.Next() { + var name string + if err := rows.Scan(&name); err != nil { + return wrap(err) + } + if err := s.recalcGlobalForFile(txp, name); err != nil { + return wrap(err) + } + } + return wrap(rows.Err()) +} + +func (s *folderDB) recalcGlobalForFile(txp *txPreparedStmts, file string) error { + //nolint:sqlclosecheck + selStmt, err := txp.Preparex(` + SELECT name, device_idx, sequence, modified, version, deleted, invalid, local_flags FROM files + WHERE name = ? + `) + if err != nil { + return wrap(err) + } + es, err := itererr.Collect(iterStructs[fileRow](selStmt.Queryx(file))) + if err != nil { + return wrap(err) + } + if len(es) == 0 { + // shouldn't happen + return nil + } + + // Sort the entries; the global entry is at the head of the list + slices.SortFunc(es, fileRow.Compare) + + // The global version is the first one in the list that is not invalid, + // or just the first one in the list if all are invalid. + var global fileRow + globIdx := slices.IndexFunc(es, func(e fileRow) bool { return !e.Invalid }) + if globIdx < 0 { + globIdx = 0 + } + global = es[globIdx] + + // We "have" the file if the position in the list of versions is at the + // global version or better, or if the version is the same as the global + // file (we might be further down the list due to invalid flags), or if + // the global is deleted and we don't have it at all... + localIdx := slices.IndexFunc(es, func(e fileRow) bool { return e.DeviceIdx == s.localDeviceIdx }) + hasLocal := localIdx >= 0 && localIdx <= globIdx || // have a better or equal version + localIdx >= 0 && es[localIdx].Version.Equal(global.Version.Vector) || // have an equal version but invalid/ignored + localIdx < 0 && global.Deleted // missing it, but the global is also deleted + + // Set the global flag on the global entry. Set the need flag if the + // local device needs this file, unless it's invalid. + global.LocalFlags |= protocol.FlagLocalGlobal + if hasLocal || global.Invalid { + global.LocalFlags &= ^protocol.FlagLocalNeeded + } else { + global.LocalFlags |= protocol.FlagLocalNeeded + } + //nolint:sqlclosecheck + upStmt, err := txp.Preparex(` + UPDATE files SET local_flags = ? + WHERE device_idx = ? AND sequence = ? + `) + if err != nil { + return wrap(err) + } + if _, err := upStmt.Exec(global.LocalFlags, global.DeviceIdx, global.Sequence); err != nil { + return wrap(err) + } + + // Clear the need and global flags on all other entries + //nolint:sqlclosecheck + upStmt, err = txp.Preparex(` + UPDATE files SET local_flags = local_flags & ? + WHERE name = ? AND sequence != ? AND local_flags & ? != 0 + `) + if err != nil { + return wrap(err) + } + if _, err := upStmt.Exec(^(protocol.FlagLocalNeeded | protocol.FlagLocalGlobal), global.Name, global.Sequence, protocol.FlagLocalNeeded|protocol.FlagLocalGlobal); err != nil { + return wrap(err) + } + + return nil +} + +func (s *DB) folderIdxLocked(folderID string) (int64, error) { + if _, err := s.stmt(` + INSERT OR IGNORE INTO folders(folder_id) + VALUES (?) + `).Exec(folderID); err != nil { + return 0, wrap(err) + } + var idx int64 + if err := s.stmt(` + SELECT idx FROM folders + WHERE folder_id = ? + `).Get(&idx, folderID); err != nil { + return 0, wrap(err) + } + + return idx, nil +} + +type fileRow struct { + Name string + Version dbVector + DeviceIdx int64 `db:"device_idx"` + Sequence int64 + Modified int64 + Size int64 + LocalFlags int64 `db:"local_flags"` + Deleted bool + Invalid bool +} + +func (e fileRow) Compare(other fileRow) int { + // From FileInfo.WinsConflict + vc := e.Version.Compare(other.Version.Vector) + switch vc { + case protocol.Equal: + if e.Invalid != other.Invalid { + if e.Invalid { + return 1 + } + return -1 + } + + // Compare the device ID index, lower is better. This is only + // deterministic to the extent that LocalDeviceID will always be the + // lowest one, order between remote devices is random (and + // irrelevant). + return cmp.Compare(e.DeviceIdx, other.DeviceIdx) + case protocol.Greater: // we are newer + return -1 + case protocol.Lesser: // we are older + return 1 + case protocol.ConcurrentGreater, protocol.ConcurrentLesser: // there is a conflict + if e.Invalid != other.Invalid { + if e.Invalid { // we are invalid, we lose + return 1 + } + return -1 // they are invalid, we win + } + if e.Deleted != other.Deleted { + if e.Deleted { // we are deleted, we lose + return 1 + } + return -1 // they are deleted, we win + } + if d := cmp.Compare(e.Modified, other.Modified); d != 0 { + return -d // positive d means we were newer, so we win (negative return) + } + if vc == protocol.ConcurrentGreater { + return -1 // we have a better device ID, we win + } + return 1 // they win + default: + return 0 + } +} + +func (s *folderDB) periodicCheckpointLocked(fs []protocol.FileInfo) { + // Induce periodic checkpoints. We add points for each file and block, + // and checkpoint when we've written more than a threshold of points. + // This ensures we do not go too long without a checkpoint, while also + // not doing it incessantly for every update. + s.updatePoints += updatePointsPerFile * len(fs) + for _, f := range fs { + s.updatePoints += len(f.Blocks) * updatePointsPerBlock + } + if s.updatePoints > updatePointsThreshold { + conn, err := s.sql.Conn(context.Background()) + if err != nil { + l.Debugln(s.baseName, "conn:", err) + return + } + defer conn.Close() + if _, err := conn.ExecContext(context.Background(), `PRAGMA journal_size_limit = 8388608`); err != nil { + l.Debugln(s.baseName, "PRAGMA journal_size_limit:", err) + } + + // Every 50th checkpoint becomes a truncate, in an effort to bring + // down the size now and then. + checkpointType := "RESTART" + if s.checkpointsCount > 50 { + checkpointType = "TRUNCATE" + } + cmd := fmt.Sprintf(`PRAGMA wal_checkpoint(%s)`, checkpointType) + row := conn.QueryRowContext(context.Background(), cmd) + + var res, modified, moved int + if row.Err() != nil { + l.Debugln(s.baseName, cmd+":", err) + } else if err := row.Scan(&res, &modified, &moved); err != nil { + l.Debugln(s.baseName, cmd+" (scan):", err) + } else { + l.Debugln(s.baseName, cmd, s.checkpointsCount, "at", s.updatePoints, "returned", res, modified, moved) + } + + // Reset the truncate counter when a truncate succeeded. If it + // failed, we'll keep trying it until we succeed. Increase it faster + // when we fail to checkpoint, as it's more likely the WAL is + // growing and will need truncation when we get out of this state. + switch { + case res == 1: + s.checkpointsCount += 10 + case res == 0 && checkpointType == "TRUNCATE": + s.checkpointsCount = 0 + default: + s.checkpointsCount++ + } + s.updatePoints = 0 + } +} diff --git a/internal/db/sqlite/sql/README.md b/internal/db/sqlite/sql/README.md new file mode 100644 index 000000000..42929fd51 --- /dev/null +++ b/internal/db/sqlite/sql/README.md @@ -0,0 +1,8 @@ +These SQL scripts are embedded in the binary. + +Scripts in `schema/` are run at every startup, in alphanumerical order. + +Scripts in `migrations/` are run when a migration is needed; the must begin +with a number that equals the schema version that results from that +migration. Migrations are not run on initial database creation, so the +scripts in `schema/` should create the latest version. diff --git a/internal/db/sqlite/sql/migrations/01-placeholder.sql b/internal/db/sqlite/sql/migrations/01-placeholder.sql new file mode 100644 index 000000000..30ec3e8c3 --- /dev/null +++ b/internal/db/sqlite/sql/migrations/01-placeholder.sql @@ -0,0 +1,7 @@ +-- Copyright (C) 2025 The Syncthing Authors. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at https://mozilla.org/MPL/2.0/. + +-- The next migration should be number two. \ No newline at end of file diff --git a/internal/db/sqlite/sql/schema/common/10-schema.sql b/internal/db/sqlite/sql/schema/common/10-schema.sql new file mode 100644 index 000000000..ce636dbd4 --- /dev/null +++ b/internal/db/sqlite/sql/schema/common/10-schema.sql @@ -0,0 +1,14 @@ +-- Copyright (C) 2025 The Syncthing Authors. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at https://mozilla.org/MPL/2.0/. + +-- Schema migrations hold the list of historical migrations applied +CREATE TABLE IF NOT EXISTS schemamigrations ( + schema_version INTEGER NOT NULL, + applied_at INTEGER NOT NULL, -- unix nanos + syncthing_version TEXT NOT NULL COLLATE BINARY, + PRIMARY KEY(schema_version) +) STRICT +; diff --git a/internal/db/sqlite/sql/schema/common/70-kv.sql b/internal/db/sqlite/sql/schema/common/70-kv.sql new file mode 100644 index 000000000..e723ef467 --- /dev/null +++ b/internal/db/sqlite/sql/schema/common/70-kv.sql @@ -0,0 +1,13 @@ +-- Copyright (C) 2025 The Syncthing Authors. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at https://mozilla.org/MPL/2.0/. + +--- Simple KV store. This backs the "miscDB" we use for certain minor pieces +-- of data. +CREATE TABLE IF NOT EXISTS kv ( + key TEXT NOT NULL PRIMARY KEY COLLATE BINARY, + value BLOB NOT NULL +) STRICT +; diff --git a/internal/db/sqlite/sql/schema/folder/00-devices.sql b/internal/db/sqlite/sql/schema/folder/00-devices.sql new file mode 100644 index 000000000..61e4a26ee --- /dev/null +++ b/internal/db/sqlite/sql/schema/folder/00-devices.sql @@ -0,0 +1,12 @@ +-- Copyright (C) 2025 The Syncthing Authors. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at https://mozilla.org/MPL/2.0/. + +-- devices map device IDs as used by Syncthing to database device indexes +CREATE TABLE IF NOT EXISTS devices ( + idx INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + device_id TEXT NOT NULL UNIQUE COLLATE BINARY +) STRICT +; diff --git a/internal/db/sqlite/sql/schema/folder/20-files.sql b/internal/db/sqlite/sql/schema/folder/20-files.sql new file mode 100644 index 000000000..d826f6340 --- /dev/null +++ b/internal/db/sqlite/sql/schema/folder/20-files.sql @@ -0,0 +1,60 @@ +-- Copyright (C) 2025 The Syncthing Authors. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at https://mozilla.org/MPL/2.0/. + +-- Files +-- +-- The files table contains all files announced by any device. Files present +-- on this device are filed under the LocalDeviceID, not the actual current +-- device ID, for simplicity, consistency and portability. One announced +-- version of each file is considered the "global" version - the latest one, +-- that all other devices strive to replicate. This instance gets the Global +-- flag bit set. There may be other identical instances of this file +-- announced by other devices, but only one onstance gets the Global flag; +-- this simplifies accounting. If the current device has the Global version, +-- the LocalDeviceID instance of the file is the one that has the Global +-- bit. +-- +-- If the current device does not have that version of the file it gets the +-- Need bit set. Only Global files announced by another device can have the +-- Need bit. This allows for very efficient lookup of files needing handling +-- on this device, which is a common query. +CREATE TABLE IF NOT EXISTS files ( + device_idx INTEGER NOT NULL, -- actual device ID or LocalDeviceID + sequence INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, -- our local database sequence, for each and every entry + remote_sequence INTEGER, -- remote device's sequence number, null for local or synthetic entries + name TEXT NOT NULL COLLATE BINARY, + type INTEGER NOT NULL, -- protocol.FileInfoType + modified INTEGER NOT NULL, -- Unix nanos + size INTEGER NOT NULL, + version TEXT NOT NULL COLLATE BINARY, + deleted INTEGER NOT NULL, -- boolean + invalid INTEGER NOT NULL, -- boolean + local_flags INTEGER NOT NULL, + blocklist_hash BLOB, -- null when there are no blocks + FOREIGN KEY(device_idx) REFERENCES devices(idx) ON DELETE CASCADE +) STRICT +; +-- FileInfos store the actual protobuf object. We do this separately to keep +-- the files rows smaller and more efficient. +CREATE TABLE IF NOT EXISTS fileinfos ( + sequence INTEGER NOT NULL PRIMARY KEY, -- our local database sequence from the files table + fiprotobuf BLOB NOT NULL, + FOREIGN KEY(sequence) REFERENCES files(sequence) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED +) STRICT +; +-- There can be only one file per folder, device, and remote sequence number +CREATE UNIQUE INDEX IF NOT EXISTS files_remote_sequence ON files (device_idx, remote_sequence) + WHERE remote_sequence IS NOT NULL +; +-- There can be only one file per folder, device, and name +CREATE UNIQUE INDEX IF NOT EXISTS files_device_name ON files (device_idx, name) +; +-- We want to be able to look up & iterate files based on just folder and name +CREATE INDEX IF NOT EXISTS files_name_only ON files (name) +; +-- We want to be able to look up & iterate files based on blocks hash +CREATE INDEX IF NOT EXISTS files_blocklist_hash_only ON files (blocklist_hash, device_idx) WHERE blocklist_hash IS NOT NULL +; diff --git a/internal/db/sqlite/sql/schema/folder/30-indexids.sql b/internal/db/sqlite/sql/schema/folder/30-indexids.sql new file mode 100644 index 000000000..e6a40bb53 --- /dev/null +++ b/internal/db/sqlite/sql/schema/folder/30-indexids.sql @@ -0,0 +1,22 @@ +-- Copyright (C) 2025 The Syncthing Authors. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at https://mozilla.org/MPL/2.0/. + +-- indexids holds the index ID and maximum sequence for a given device and folder +CREATE TABLE IF NOT EXISTS indexids ( + device_idx INTEGER NOT NULL, + index_id TEXT NOT NULL COLLATE BINARY, + sequence INTEGER NOT NULL DEFAULT 0, + PRIMARY KEY(device_idx), + FOREIGN KEY(device_idx) REFERENCES devices(idx) ON DELETE CASCADE +) STRICT, WITHOUT ROWID +; +CREATE TRIGGER IF NOT EXISTS indexids_seq AFTER INSERT ON files +BEGIN + INSERT INTO indexids (device_idx, index_id, sequence) + VALUES (NEW.device_idx, "", COALESCE(NEW.remote_sequence, NEW.sequence)) + ON CONFLICT DO UPDATE SET sequence = COALESCE(NEW.remote_sequence, NEW.sequence); +END +; diff --git a/internal/db/sqlite/sql/schema/folder/40-counts.sql b/internal/db/sqlite/sql/schema/folder/40-counts.sql new file mode 100644 index 000000000..afa50710d --- /dev/null +++ b/internal/db/sqlite/sql/schema/folder/40-counts.sql @@ -0,0 +1,47 @@ +-- Copyright (C) 2025 The Syncthing Authors. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at https://mozilla.org/MPL/2.0/. + +-- Counts +-- +-- Counts and sizes are maintained for each device, folder, type, flag bits +-- combination. +CREATE TABLE IF NOT EXISTS counts ( + device_idx INTEGER NOT NULL, + type INTEGER NOT NULL, + local_flags INTEGER NOT NULL, + deleted INTEGER NOT NULL, -- boolean + count INTEGER NOT NULL, + size INTEGER NOT NULL, + PRIMARY KEY(device_idx, type, local_flags, deleted), + FOREIGN KEY(device_idx) REFERENCES devices(idx) ON DELETE CASCADE +) STRICT, WITHOUT ROWID +; + +--- Maintain counts when files are added and removed using triggers + +CREATE TRIGGER IF NOT EXISTS counts_insert AFTER INSERT ON files +BEGIN + INSERT INTO counts (device_idx, type, local_flags, deleted, count, size) + VALUES (NEW.device_idx, NEW.type, NEW.local_flags, NEW.deleted, 1, NEW.size) + ON CONFLICT DO UPDATE SET count = count + 1, size = size + NEW.size; +END +; +CREATE TRIGGER IF NOT EXISTS counts_delete AFTER DELETE ON files +BEGIN + UPDATE counts SET count = count - 1, size = size - OLD.size + WHERE device_idx = OLD.device_idx AND type = OLD.type AND local_flags = OLD.local_flags AND deleted = OLD.deleted; +END +; +CREATE TRIGGER IF NOT EXISTS counts_update AFTER UPDATE OF local_flags ON files +WHEN NEW.local_flags != OLD.local_flags +BEGIN + INSERT INTO counts (device_idx, type, local_flags, deleted, count, size) + VALUES (NEW.device_idx, NEW.type, NEW.local_flags, NEW.deleted, 1, NEW.size) + ON CONFLICT DO UPDATE SET count = count + 1, size = size + NEW.size; + UPDATE counts SET count = count - 1, size = size - OLD.size + WHERE device_idx = OLD.device_idx AND type = OLD.type AND local_flags = OLD.local_flags AND deleted = OLD.deleted; +END +; diff --git a/internal/db/sqlite/sql/schema/folder/50-blocks.sql b/internal/db/sqlite/sql/schema/folder/50-blocks.sql new file mode 100644 index 000000000..17c8e40a0 --- /dev/null +++ b/internal/db/sqlite/sql/schema/folder/50-blocks.sql @@ -0,0 +1,34 @@ +-- Copyright (C) 2025 The Syncthing Authors. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at https://mozilla.org/MPL/2.0/. + +-- Block lists +-- +-- The block lists are extracted from FileInfos and stored separately. This +-- reduces the database size by reusing the same block list entry for all +-- devices announcing the same file. Doing it for all block lists instead of +-- using a size cutoff simplifies queries. Block lists are garbage collected +-- "manually", not using a trigger as that was too performance impacting. +CREATE TABLE IF NOT EXISTS blocklists ( + blocklist_hash BLOB NOT NULL PRIMARY KEY, + blprotobuf BLOB NOT NULL +) STRICT +; + +-- Blocks +-- +-- For all local files we store the blocks individually for quick lookup. A +-- given block can exist in multiple blocklists and at multiple offsets in a +-- blocklist. +CREATE TABLE IF NOT EXISTS blocks ( + hash BLOB NOT NULL, + blocklist_hash BLOB NOT NULL, + idx INTEGER NOT NULL, + offset INTEGER NOT NULL, + size INTEGER NOT NULL, + PRIMARY KEY (hash, blocklist_hash, idx), + FOREIGN KEY(blocklist_hash) REFERENCES blocklists(blocklist_hash) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED +) STRICT +; diff --git a/internal/db/sqlite/sql/schema/folder/50-mtimes.sql b/internal/db/sqlite/sql/schema/folder/50-mtimes.sql new file mode 100644 index 000000000..5b3313c10 --- /dev/null +++ b/internal/db/sqlite/sql/schema/folder/50-mtimes.sql @@ -0,0 +1,14 @@ +-- Copyright (C) 2025 The Syncthing Authors. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at https://mozilla.org/MPL/2.0/. + +--- Backing for the MtimeFS +CREATE TABLE IF NOT EXISTS mtimes ( + name TEXT NOT NULL, + ondisk INTEGER NOT NULL, -- unix nanos + virtual INTEGER NOT NULL, -- unix nanos + PRIMARY KEY(name) +) STRICT, WITHOUT ROWID +; diff --git a/internal/db/sqlite/sql/schema/main/00-folders.sql b/internal/db/sqlite/sql/schema/main/00-folders.sql new file mode 100644 index 000000000..72f20ab13 --- /dev/null +++ b/internal/db/sqlite/sql/schema/main/00-folders.sql @@ -0,0 +1,16 @@ +-- Copyright (C) 2025 The Syncthing Authors. +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at https://mozilla.org/MPL/2.0/. + +-- folders map folder IDs as used by Syncthing to database folder indexes +CREATE TABLE IF NOT EXISTS folders ( + idx INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + folder_id TEXT NOT NULL UNIQUE COLLATE BINARY, + database_name TEXT COLLATE BINARY -- initially null +) STRICT +; +-- The database_name is unique, when set +CREATE INDEX IF NOT EXISTS folders_database_name ON folders (database_name) WHERE database_name IS NOT NULL +; diff --git a/internal/db/sqlite/util.go b/internal/db/sqlite/util.go new file mode 100644 index 000000000..dfed2857c --- /dev/null +++ b/internal/db/sqlite/util.go @@ -0,0 +1,124 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package sqlite + +import ( + "cmp" + "database/sql/driver" + "errors" + "iter" + "slices" + + "github.com/jmoiron/sqlx" + "github.com/syncthing/syncthing/internal/gen/bep" + "github.com/syncthing/syncthing/internal/gen/dbproto" + "github.com/syncthing/syncthing/lib/osutil" + "github.com/syncthing/syncthing/lib/protocol" + "google.golang.org/protobuf/proto" +) + +// iterStructs returns an iterator over the given struct type by scanning +// the SQL rows. `rows` is closed when the iterator exits. +func iterStructs[T any](rows *sqlx.Rows, err error) (iter.Seq[T], func() error) { + if err != nil { + return func(_ func(T) bool) {}, func() error { return err } + } + + var retErr error + return func(yield func(T) bool) { + defer rows.Close() + for rows.Next() { + v := new(T) + if err := rows.StructScan(v); err != nil { + retErr = err + break + } + if cleanuper, ok := any(v).(interface{ cleanup() }); ok { + cleanuper.cleanup() + } + if !yield(*v) { + return + } + } + if err := rows.Err(); err != nil && retErr == nil { + retErr = err + } + }, func() error { return retErr } +} + +// dbVector is a wrapper that allows protocol.Vector values to be serialized +// to and from the database. +type dbVector struct { //nolint:recvcheck + protocol.Vector +} + +func (v dbVector) Value() (driver.Value, error) { + return v.String(), nil +} + +func (v *dbVector) Scan(value any) error { + str, ok := value.(string) + if !ok { + return errors.New("not a string") + } + if str == "" { + v.Vector = protocol.Vector{} + return nil + } + vec, err := protocol.VectorFromString(str) + if err != nil { + return wrap(err) + } + + // This is only necessary because I messed up counter serialisation and + // thereby ordering in 2.0.0 betas, and can be removed in the future. + slices.SortFunc(vec.Counters, func(a, b protocol.Counter) int { return cmp.Compare(a.ID, b.ID) }) + + v.Vector = vec + + return nil +} + +// indirectFI constructs a FileInfo from separate marshalled FileInfo and +// BlockList bytes. +type indirectFI struct { + Name string // not used, must be present as dest for Need iterator + FiProtobuf []byte + BlProtobuf []byte + Size int64 // not used + Modified int64 // not used +} + +func (i indirectFI) FileInfo() (protocol.FileInfo, error) { + var fi bep.FileInfo + if err := proto.Unmarshal(i.FiProtobuf, &fi); err != nil { + return protocol.FileInfo{}, wrap(err, "unmarshal fileinfo") + } + if len(i.BlProtobuf) > 0 { + var bl dbproto.BlockList + if err := proto.Unmarshal(i.BlProtobuf, &bl); err != nil { + return protocol.FileInfo{}, wrap(err, "unmarshal blocklist") + } + fi.Blocks = bl.Blocks + } + fi.Name = osutil.NativeFilename(fi.Name) + return protocol.FileInfoFromDB(&fi), nil +} + +func prefixEnd(s string) string { + if s == "" { + panic("bug: cannot represent end prefix for empty string") + } + bs := []byte(s) + for i := len(bs) - 1; i >= 0; i-- { + if bs[i] < 0xff { + bs[i]++ + break + } + } + return string(bs) +} diff --git a/internal/db/sqlite/util_test.go b/internal/db/sqlite/util_test.go new file mode 100644 index 000000000..eb78a5782 --- /dev/null +++ b/internal/db/sqlite/util_test.go @@ -0,0 +1,33 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package sqlite + +import ( + "testing" + + "github.com/syncthing/syncthing/lib/protocol" +) + +func TestDbvector(t *testing.T) { + vec := protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 7}, {ID: 123456789, Value: 42424242}}} + dbVec := dbVector{vec} + val, err := dbVec.Value() + if err != nil { + t.Fatal(val) + } + + var dbVec2 dbVector + if err := dbVec2.Scan(val); err != nil { + t.Fatal(err) + } + + if !dbVec2.Vector.Equal(vec) { + t.Log(vec) + t.Log(dbVec2.Vector) + t.Fatal("should match") + } +} diff --git a/internal/db/typed.go b/internal/db/typed.go new file mode 100644 index 000000000..6180e12b7 --- /dev/null +++ b/internal/db/typed.go @@ -0,0 +1,140 @@ +// Copyright (C) 2014 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package db + +import ( + "database/sql" + "encoding/binary" + "errors" + "time" +) + +// Typed is a simple key-value store using a specific namespace within a +// lower level KV. +type Typed struct { + db KV + prefix string +} + +func NewMiscDB(db KV) *Typed { + return NewTyped(db, "misc") +} + +// NewTyped returns a new typed key-value store that lives in the namespace +// specified by the prefix. +func NewTyped(db KV, prefix string) *Typed { + return &Typed{ + db: db, + prefix: prefix, + } +} + +// PutInt64 stores a new int64. Any existing value (even if of another type) +// is overwritten. +func (n *Typed) PutInt64(key string, val int64) error { + var valBs [8]byte + binary.BigEndian.PutUint64(valBs[:], uint64(val)) //nolint:gosec + return n.db.PutKV(n.prefixedKey(key), valBs[:]) +} + +// Int64 returns the stored value interpreted as an int64 and a boolean that +// is false if no value was stored at the key. +func (n *Typed) Int64(key string) (int64, bool, error) { + valBs, err := n.db.GetKV(n.prefixedKey(key)) + if err != nil { + return 0, false, filterNotFound(err) + } + val := binary.BigEndian.Uint64(valBs) + return int64(val), true, nil //nolint:gosec +} + +// PutTime stores a new time.Time. Any existing value (even if of another +// type) is overwritten. +func (n *Typed) PutTime(key string, val time.Time) error { + valBs, _ := val.MarshalBinary() // never returns an error + return n.db.PutKV(n.prefixedKey(key), valBs) +} + +// Time returns the stored value interpreted as a time.Time and a boolean +// that is false if no value was stored at the key. +func (n *Typed) Time(key string) (time.Time, bool, error) { + var t time.Time + valBs, err := n.db.GetKV(n.prefixedKey(key)) + if err != nil { + return t, false, filterNotFound(err) + } + err = t.UnmarshalBinary(valBs) + return t, err == nil, err +} + +// PutString stores a new string. Any existing value (even if of another type) +// is overwritten. +func (n *Typed) PutString(key, val string) error { + return n.db.PutKV(n.prefixedKey(key), []byte(val)) +} + +// String returns the stored value interpreted as a string and a boolean that +// is false if no value was stored at the key. +func (n *Typed) String(key string) (string, bool, error) { + valBs, err := n.db.GetKV(n.prefixedKey(key)) + if err != nil { + return "", false, filterNotFound(err) + } + return string(valBs), true, nil +} + +// PutBytes stores a new byte slice. Any existing value (even if of another type) +// is overwritten. +func (n *Typed) PutBytes(key string, val []byte) error { + return n.db.PutKV(n.prefixedKey(key), val) +} + +// Bytes returns the stored value as a raw byte slice and a boolean that +// is false if no value was stored at the key. +func (n *Typed) Bytes(key string) ([]byte, bool, error) { + valBs, err := n.db.GetKV(n.prefixedKey(key)) + if err != nil { + return nil, false, filterNotFound(err) + } + return valBs, true, nil +} + +// PutBool stores a new boolean. Any existing value (even if of another type) +// is overwritten. +func (n *Typed) PutBool(key string, val bool) error { + if val { + return n.db.PutKV(n.prefixedKey(key), []byte{0x0}) + } + return n.db.PutKV(n.prefixedKey(key), []byte{0x1}) +} + +// Bool returns the stored value as a boolean and a boolean that +// is false if no value was stored at the key. +func (n *Typed) Bool(key string) (bool, bool, error) { + valBs, err := n.db.GetKV(n.prefixedKey(key)) + if err != nil { + return false, false, filterNotFound(err) + } + return valBs[0] == 0x0, true, nil +} + +// Delete deletes the specified key. It is allowed to delete a nonexistent +// key. +func (n *Typed) Delete(key string) error { + return n.db.DeleteKV(n.prefixedKey(key)) +} + +func (n *Typed) prefixedKey(key string) string { + return n.prefix + "/" + key +} + +func filterNotFound(err error) error { + if errors.Is(err, sql.ErrNoRows) { + return nil + } + return err +} diff --git a/internal/db/typed_test.go b/internal/db/typed_test.go new file mode 100644 index 000000000..9f01b4335 --- /dev/null +++ b/internal/db/typed_test.go @@ -0,0 +1,115 @@ +// Copyright (C) 2014 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package db_test + +import ( + "testing" + "time" + + "github.com/syncthing/syncthing/internal/db" + "github.com/syncthing/syncthing/internal/db/sqlite" +) + +func TestNamespacedInt(t *testing.T) { + t.Parallel() + + ldb, err := sqlite.OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + ldb.Close() + }) + + n1 := db.NewTyped(ldb, "foo") + n2 := db.NewTyped(ldb, "bar") + + t.Run("Int", func(t *testing.T) { + t.Parallel() + + // Key is missing to start with + + if v, ok, err := n1.Int64("testint"); err != nil { + t.Error("Unexpected error:", err) + } else if v != 0 || ok { + t.Errorf("Incorrect return v %v != 0 || ok %v != false", v, ok) + } + + if err := n1.PutInt64("testint", 42); err != nil { + t.Fatal(err) + } + + // It should now exist in n1 + + if v, ok, err := n1.Int64("testint"); err != nil { + t.Error("Unexpected error:", err) + } else if v != 42 || !ok { + t.Errorf("Incorrect return v %v != 42 || ok %v != true", v, ok) + } + + // ... but not in n2, which is in a different namespace + + if v, ok, err := n2.Int64("testint"); err != nil { + t.Error("Unexpected error:", err) + } else if v != 0 || ok { + t.Errorf("Incorrect return v %v != 0 || ok %v != false", v, ok) + } + + if err := n1.Delete("testint"); err != nil { + t.Fatal(err) + } + + // It should no longer exist + + if v, ok, err := n1.Int64("testint"); err != nil { + t.Error("Unexpected error:", err) + } else if v != 0 || ok { + t.Errorf("Incorrect return v %v != 0 || ok %v != false", v, ok) + } + }) + + t.Run("Time", func(t *testing.T) { + t.Parallel() + + if v, ok, err := n1.Time("testtime"); err != nil { + t.Error("Unexpected error:", err) + } else if !v.IsZero() || ok { + t.Errorf("Incorrect return v %v != %v || ok %v != false", v, time.Time{}, ok) + } + + now := time.Now() + if err := n1.PutTime("testtime", now); err != nil { + t.Fatal(err) + } + + if v, ok, err := n1.Time("testtime"); err != nil { + t.Error("Unexpected error:", err) + } else if !v.Equal(now) || !ok { + t.Errorf("Incorrect return v %v != %v || ok %v != true", v, now, ok) + } + }) + + t.Run("String", func(t *testing.T) { + t.Parallel() + + if v, ok, err := n1.String("teststring"); err != nil { + t.Error("Unexpected error:", err) + } else if v != "" || ok { + t.Errorf("Incorrect return v %q != \"\" || ok %v != false", v, ok) + } + + if err := n1.PutString("teststring", "yo"); err != nil { + t.Fatal(err) + } + + if v, ok, err := n1.String("teststring"); err != nil { + t.Error("Unexpected error:", err) + } else if v != "yo" || !ok { + t.Errorf("Incorrect return v %q != \"yo\" || ok %v != true", v, ok) + } + }) +} diff --git a/internal/gen/bep/bep.pb.go b/internal/gen/bep/bep.pb.go index 686fc6c04..11c0badc3 100644 --- a/internal/gen/bep/bep.pb.go +++ b/internal/gen/bep/bep.pb.go @@ -1093,10 +1093,9 @@ type BlockInfo struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Hash []byte `protobuf:"bytes,3,opt,name=hash,proto3" json:"hash,omitempty"` - Offset int64 `protobuf:"varint,1,opt,name=offset,proto3" json:"offset,omitempty"` - Size int32 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"` - WeakHash uint32 `protobuf:"varint,4,opt,name=weak_hash,json=weakHash,proto3" json:"weak_hash,omitempty"` + Hash []byte `protobuf:"bytes,3,opt,name=hash,proto3" json:"hash,omitempty"` + Offset int64 `protobuf:"varint,1,opt,name=offset,proto3" json:"offset,omitempty"` + Size int32 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"` } func (x *BlockInfo) Reset() { @@ -1150,13 +1149,6 @@ func (x *BlockInfo) GetSize() int32 { return 0 } -func (x *BlockInfo) GetWeakHash() uint32 { - if x != nil { - return x.WeakHash - } - return 0 -} - type Vector struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1578,7 +1570,6 @@ type Request struct { Size int32 `protobuf:"varint,5,opt,name=size,proto3" json:"size,omitempty"` Hash []byte `protobuf:"bytes,6,opt,name=hash,proto3" json:"hash,omitempty"` FromTemporary bool `protobuf:"varint,7,opt,name=from_temporary,json=fromTemporary,proto3" json:"from_temporary,omitempty"` - WeakHash uint32 `protobuf:"varint,8,opt,name=weak_hash,json=weakHash,proto3" json:"weak_hash,omitempty"` BlockNo int32 `protobuf:"varint,9,opt,name=block_no,json=blockNo,proto3" json:"block_no,omitempty"` } @@ -1661,13 +1652,6 @@ func (x *Request) GetFromTemporary() bool { return false } -func (x *Request) GetWeakHash() uint32 { - if x != nil { - return x.WeakHash - } - return 0 -} - func (x *Request) GetBlockNo() int32 { if x != nil { return x.BlockNo @@ -2079,158 +2063,155 @@ var file_bep_bep_proto_rawDesc = []byte{ 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x6f, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x6e, 0x6f, - 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x68, 0x0a, 0x09, 0x42, + 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x51, 0x0a, 0x09, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x77, 0x65, 0x61, 0x6b, - 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x77, 0x65, 0x61, - 0x6b, 0x48, 0x61, 0x73, 0x68, 0x22, 0x32, 0x0a, 0x06, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, - 0x28, 0x0a, 0x08, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x0c, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x52, - 0x08, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x22, 0x2f, 0x0a, 0x07, 0x43, 0x6f, 0x75, - 0x6e, 0x74, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xfd, 0x01, 0x0a, 0x0c, 0x50, - 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x04, 0x75, - 0x6e, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x62, 0x65, 0x70, 0x2e, - 0x55, 0x6e, 0x69, 0x78, 0x44, 0x61, 0x74, 0x61, 0x52, 0x04, 0x75, 0x6e, 0x69, 0x78, 0x12, 0x2a, - 0x0a, 0x07, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x10, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x44, 0x61, 0x74, - 0x61, 0x52, 0x07, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x12, 0x24, 0x0a, 0x05, 0x6c, 0x69, - 0x6e, 0x75, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x62, 0x65, 0x70, 0x2e, - 0x58, 0x61, 0x74, 0x74, 0x72, 0x44, 0x61, 0x74, 0x61, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x75, 0x78, - 0x12, 0x26, 0x0a, 0x06, 0x64, 0x61, 0x72, 0x77, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0e, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x58, 0x61, 0x74, 0x74, 0x72, 0x44, 0x61, 0x74, 0x61, - 0x52, 0x06, 0x64, 0x61, 0x72, 0x77, 0x69, 0x6e, 0x12, 0x28, 0x0a, 0x07, 0x66, 0x72, 0x65, 0x65, - 0x62, 0x73, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x62, 0x65, 0x70, 0x2e, - 0x58, 0x61, 0x74, 0x74, 0x72, 0x44, 0x61, 0x74, 0x61, 0x52, 0x07, 0x66, 0x72, 0x65, 0x65, 0x62, - 0x73, 0x64, 0x12, 0x26, 0x0a, 0x06, 0x6e, 0x65, 0x74, 0x62, 0x73, 0x64, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x22, 0x32, + 0x0a, 0x06, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x28, 0x0a, 0x08, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x62, 0x65, 0x70, + 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x08, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, + 0x72, 0x73, 0x22, 0x2f, 0x0a, 0x07, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x22, 0xfd, 0x01, 0x0a, 0x0c, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, + 0x44, 0x61, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x04, 0x75, 0x6e, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x55, 0x6e, 0x69, 0x78, 0x44, 0x61, 0x74, + 0x61, 0x52, 0x04, 0x75, 0x6e, 0x69, 0x78, 0x12, 0x2a, 0x0a, 0x07, 0x77, 0x69, 0x6e, 0x64, 0x6f, + 0x77, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x57, + 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x44, 0x61, 0x74, 0x61, 0x52, 0x07, 0x77, 0x69, 0x6e, 0x64, + 0x6f, 0x77, 0x73, 0x12, 0x24, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x58, 0x61, 0x74, 0x74, 0x72, 0x44, 0x61, - 0x74, 0x61, 0x52, 0x06, 0x6e, 0x65, 0x74, 0x62, 0x73, 0x64, 0x22, 0x6c, 0x0a, 0x08, 0x55, 0x6e, - 0x69, 0x78, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x77, 0x6e, 0x65, - 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x69, 0x64, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x03, 0x67, 0x69, 0x64, 0x22, 0x52, 0x0a, 0x0b, 0x57, 0x69, 0x6e, 0x64, - 0x6f, 0x77, 0x73, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x77, 0x6e, - 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, - 0x69, 0x73, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, - 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x49, 0x73, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x22, 0x2f, 0x0a, 0x09, - 0x58, 0x61, 0x74, 0x74, 0x72, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x06, 0x78, 0x61, 0x74, - 0x74, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x62, 0x65, 0x70, 0x2e, - 0x58, 0x61, 0x74, 0x74, 0x72, 0x52, 0x06, 0x78, 0x61, 0x74, 0x74, 0x72, 0x73, 0x22, 0x31, 0x0a, - 0x05, 0x58, 0x61, 0x74, 0x74, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x22, 0xe4, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, - 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, - 0x6c, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, - 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, - 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, - 0x73, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x25, 0x0a, 0x0e, 0x66, 0x72, 0x6f, 0x6d, - 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0d, 0x66, 0x72, 0x6f, 0x6d, 0x54, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0x79, 0x12, - 0x1b, 0x0a, 0x09, 0x77, 0x65, 0x61, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x08, 0x77, 0x65, 0x61, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x19, 0x0a, 0x08, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x6f, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x6f, 0x22, 0x52, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x45, 0x72, 0x72, 0x6f, - 0x72, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x22, 0x65, 0x0a, 0x10, 0x44, - 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, - 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x39, 0x0a, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x46, + 0x74, 0x61, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x12, 0x26, 0x0a, 0x06, 0x64, 0x61, 0x72, + 0x77, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x62, 0x65, 0x70, 0x2e, + 0x58, 0x61, 0x74, 0x74, 0x72, 0x44, 0x61, 0x74, 0x61, 0x52, 0x06, 0x64, 0x61, 0x72, 0x77, 0x69, + 0x6e, 0x12, 0x28, 0x0a, 0x07, 0x66, 0x72, 0x65, 0x65, 0x62, 0x73, 0x64, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x58, 0x61, 0x74, 0x74, 0x72, 0x44, 0x61, + 0x74, 0x61, 0x52, 0x07, 0x66, 0x72, 0x65, 0x65, 0x62, 0x73, 0x64, 0x12, 0x26, 0x0a, 0x06, 0x6e, + 0x65, 0x74, 0x62, 0x73, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x62, 0x65, + 0x70, 0x2e, 0x58, 0x61, 0x74, 0x74, 0x72, 0x44, 0x61, 0x74, 0x61, 0x52, 0x06, 0x6e, 0x65, 0x74, + 0x62, 0x73, 0x64, 0x22, 0x6c, 0x0a, 0x08, 0x55, 0x6e, 0x69, 0x78, 0x44, 0x61, 0x74, 0x61, 0x12, + 0x1d, 0x0a, 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, + 0x0a, 0x0a, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, + 0x03, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, + 0x10, 0x0a, 0x03, 0x67, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x67, 0x69, + 0x64, 0x22, 0x52, 0x0a, 0x0b, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x44, 0x61, 0x74, 0x61, + 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x24, 0x0a, 0x0e, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x73, 0x5f, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x49, 0x73, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x22, 0x2f, 0x0a, 0x09, 0x58, 0x61, 0x74, 0x74, 0x72, 0x44, 0x61, + 0x74, 0x61, 0x12, 0x22, 0x0a, 0x06, 0x78, 0x61, 0x74, 0x74, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x58, 0x61, 0x74, 0x74, 0x72, 0x52, 0x06, + 0x78, 0x61, 0x74, 0x74, 0x72, 0x73, 0x22, 0x31, 0x0a, 0x05, 0x58, 0x61, 0x74, 0x74, 0x72, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xcd, 0x01, 0x0a, 0x07, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, + 0x68, 0x12, 0x25, 0x0a, 0x0e, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, + 0x61, 0x72, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x66, 0x72, 0x6f, 0x6d, 0x54, + 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x5f, 0x6e, 0x6f, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x4e, 0x6f, 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x22, 0x52, 0x0a, 0x08, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x04, 0x63, 0x6f, 0x64, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x45, 0x72, + 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x22, 0x65, 0x0a, + 0x10, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, + 0x73, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x39, 0x0a, 0x07, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x62, 0x65, 0x70, + 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x72, 0x6f, + 0x67, 0x72, 0x65, 0x73, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x07, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x73, 0x22, 0xe5, 0x01, 0x0a, 0x1a, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x6f, 0x77, + 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x72, 0x6f, 0x67, 0x72, - 0x65, 0x73, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x73, 0x22, 0xe5, 0x01, 0x0a, 0x1a, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x6c, - 0x6f, 0x61, 0x64, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x46, 0x69, 0x6c, - 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, - 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x62, - 0x65, 0x70, 0x2e, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, - 0x78, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x05, 0x42, 0x02, 0x10, 0x00, 0x52, 0x0c, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x06, 0x0a, 0x04, 0x50, 0x69, - 0x6e, 0x67, 0x22, 0x1f, 0x0a, 0x05, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, - 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, - 0x73, 0x6f, 0x6e, 0x2a, 0xed, 0x01, 0x0a, 0x0b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, - 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a, 0x1b, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x43, 0x4f, 0x4e, 0x46, - 0x49, 0x47, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x44, 0x45, 0x58, 0x10, 0x01, 0x12, 0x1d, 0x0a, 0x19, - 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x44, - 0x45, 0x58, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x4d, - 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x51, 0x55, - 0x45, 0x53, 0x54, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, 0x10, 0x04, - 0x12, 0x22, 0x0a, 0x1e, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, - 0x53, 0x53, 0x10, 0x05, 0x12, 0x15, 0x0a, 0x11, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x49, 0x4e, 0x47, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x4d, - 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4c, 0x4f, 0x53, - 0x45, 0x10, 0x07, 0x2a, 0x4f, 0x0a, 0x12, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, - 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x18, 0x4d, 0x45, 0x53, + 0x65, 0x73, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, + 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x05, 0x42, 0x02, 0x10, 0x00, 0x52, + 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x12, 0x1d, 0x0a, + 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x06, 0x0a, 0x04, + 0x50, 0x69, 0x6e, 0x67, 0x22, 0x1f, 0x0a, 0x05, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, + 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x2a, 0xed, 0x01, 0x0a, 0x0b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a, 0x1b, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x43, 0x4f, + 0x4e, 0x46, 0x49, 0x47, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, + 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x44, 0x45, 0x58, 0x10, 0x01, 0x12, 0x1d, + 0x0a, 0x19, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, + 0x4e, 0x44, 0x45, 0x58, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x02, 0x12, 0x18, 0x0a, + 0x14, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, + 0x51, 0x55, 0x45, 0x53, 0x54, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x4d, 0x45, 0x53, 0x53, 0x41, + 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, + 0x10, 0x04, 0x12, 0x22, 0x0a, 0x1e, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x50, 0x52, 0x4f, 0x47, + 0x52, 0x45, 0x53, 0x53, 0x10, 0x05, 0x12, 0x15, 0x0a, 0x11, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, + 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x49, 0x4e, 0x47, 0x10, 0x06, 0x12, 0x16, 0x0a, + 0x12, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4c, + 0x4f, 0x53, 0x45, 0x10, 0x07, 0x2a, 0x4f, 0x0a, 0x12, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x18, 0x4d, + 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, + 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, - 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x4d, 0x45, 0x53, 0x53, 0x41, - 0x47, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x4c, - 0x5a, 0x34, 0x10, 0x01, 0x2a, 0x56, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x14, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, - 0x4f, 0x4e, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x44, 0x41, 0x54, 0x41, 0x10, 0x00, 0x12, 0x15, 0x0a, - 0x11, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x4e, 0x45, 0x56, - 0x45, 0x52, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, - 0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x4c, 0x57, 0x41, 0x59, 0x53, 0x10, 0x02, 0x2a, 0xb0, 0x01, 0x0a, - 0x0c, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x54, 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, - 0x13, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x46, 0x49, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x49, - 0x4e, 0x46, 0x4f, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x4f, - 0x52, 0x59, 0x10, 0x01, 0x12, 0x23, 0x0a, 0x1b, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x49, 0x4e, 0x46, - 0x4f, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x59, 0x4d, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x46, - 0x49, 0x4c, 0x45, 0x10, 0x02, 0x1a, 0x02, 0x08, 0x01, 0x12, 0x28, 0x0a, 0x20, 0x46, 0x49, 0x4c, - 0x45, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x59, 0x4d, 0x4c, - 0x49, 0x4e, 0x4b, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x59, 0x10, 0x03, 0x1a, - 0x02, 0x08, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x49, 0x4e, 0x46, 0x4f, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x59, 0x4d, 0x4c, 0x49, 0x4e, 0x4b, 0x10, 0x04, 0x2a, - 0x76, 0x0a, 0x09, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x17, 0x0a, 0x13, - 0x45, 0x52, 0x52, 0x4f, 0x52, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x4e, 0x4f, 0x5f, 0x45, 0x52, - 0x52, 0x4f, 0x52, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x5f, 0x43, - 0x4f, 0x44, 0x45, 0x5f, 0x47, 0x45, 0x4e, 0x45, 0x52, 0x49, 0x43, 0x10, 0x01, 0x12, 0x1b, 0x0a, - 0x17, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x4e, 0x4f, 0x5f, 0x53, - 0x55, 0x43, 0x48, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x45, 0x52, - 0x52, 0x4f, 0x52, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, - 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x03, 0x2a, 0x7e, 0x0a, 0x1e, 0x46, 0x69, 0x6c, 0x65, 0x44, - 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2d, 0x0a, 0x29, 0x46, 0x49, 0x4c, - 0x45, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, - 0x45, 0x53, 0x53, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x41, 0x50, 0x50, 0x45, 0x4e, 0x44, 0x10, 0x00, 0x12, 0x2d, 0x0a, 0x29, 0x46, 0x49, 0x4c, 0x45, - 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, - 0x53, 0x53, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, - 0x4f, 0x52, 0x47, 0x45, 0x54, 0x10, 0x01, 0x42, 0x70, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x2e, 0x62, - 0x65, 0x70, 0x42, 0x08, 0x42, 0x65, 0x70, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2f, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x74, - 0x68, 0x69, 0x6e, 0x67, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x2f, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x62, 0x65, 0x70, 0xa2, - 0x02, 0x03, 0x42, 0x58, 0x58, 0xaa, 0x02, 0x03, 0x42, 0x65, 0x70, 0xca, 0x02, 0x03, 0x42, 0x65, - 0x70, 0xe2, 0x02, 0x0f, 0x42, 0x65, 0x70, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0xea, 0x02, 0x03, 0x42, 0x65, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x5f, 0x4c, 0x5a, 0x34, 0x10, 0x01, 0x2a, 0x56, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x14, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, + 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x44, 0x41, 0x54, 0x41, 0x10, 0x00, 0x12, + 0x15, 0x0a, 0x11, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x4e, + 0x45, 0x56, 0x45, 0x52, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, + 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x4c, 0x57, 0x41, 0x59, 0x53, 0x10, 0x02, 0x2a, 0xb0, + 0x01, 0x0a, 0x0c, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x17, 0x0a, 0x13, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x46, 0x49, 0x4c, 0x45, + 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, + 0x54, 0x4f, 0x52, 0x59, 0x10, 0x01, 0x12, 0x23, 0x0a, 0x1b, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x49, + 0x4e, 0x46, 0x4f, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x59, 0x4d, 0x4c, 0x49, 0x4e, 0x4b, + 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x02, 0x1a, 0x02, 0x08, 0x01, 0x12, 0x28, 0x0a, 0x20, 0x46, + 0x49, 0x4c, 0x45, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x59, + 0x4d, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x59, 0x10, + 0x03, 0x1a, 0x02, 0x08, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x49, 0x4e, + 0x46, 0x4f, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x59, 0x4d, 0x4c, 0x49, 0x4e, 0x4b, 0x10, + 0x04, 0x2a, 0x76, 0x0a, 0x09, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x17, + 0x0a, 0x13, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x4e, 0x4f, 0x5f, + 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x45, 0x52, 0x52, 0x4f, 0x52, + 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x47, 0x45, 0x4e, 0x45, 0x52, 0x49, 0x43, 0x10, 0x01, 0x12, + 0x1b, 0x0a, 0x17, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x4e, 0x4f, + 0x5f, 0x53, 0x55, 0x43, 0x48, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, + 0x45, 0x52, 0x52, 0x4f, 0x52, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, + 0x49, 0x44, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x03, 0x2a, 0x7e, 0x0a, 0x1e, 0x46, 0x69, 0x6c, + 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, + 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2d, 0x0a, 0x29, 0x46, + 0x49, 0x4c, 0x45, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x50, 0x52, 0x4f, + 0x47, 0x52, 0x45, 0x53, 0x53, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x41, 0x50, 0x50, 0x45, 0x4e, 0x44, 0x10, 0x00, 0x12, 0x2d, 0x0a, 0x29, 0x46, 0x49, + 0x4c, 0x45, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x50, 0x52, 0x4f, 0x47, + 0x52, 0x45, 0x53, 0x53, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x46, 0x4f, 0x52, 0x47, 0x45, 0x54, 0x10, 0x01, 0x42, 0x70, 0x0a, 0x07, 0x63, 0x6f, 0x6d, + 0x2e, 0x62, 0x65, 0x70, 0x42, 0x08, 0x42, 0x65, 0x70, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, + 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x79, 0x6e, + 0x63, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x74, 0x68, 0x69, 0x6e, 0x67, + 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x62, 0x65, + 0x70, 0xa2, 0x02, 0x03, 0x42, 0x58, 0x58, 0xaa, 0x02, 0x03, 0x42, 0x65, 0x70, 0xca, 0x02, 0x03, + 0x42, 0x65, 0x70, 0xe2, 0x02, 0x0f, 0x42, 0x65, 0x70, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x03, 0x42, 0x65, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( diff --git a/internal/itererr/itererr.go b/internal/itererr/itererr.go new file mode 100644 index 000000000..86278a159 --- /dev/null +++ b/internal/itererr/itererr.go @@ -0,0 +1,83 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package itererr + +import "iter" + +// Collect returns a slice of the items from the iterator, plus the error if +// any. +func Collect[T any](it iter.Seq[T], errFn func() error) ([]T, error) { + var s []T + for v := range it { + s = append(s, v) + } + return s, errFn() +} + +// Zip interleaves the iterator value with the error. The iteration ends +// after a non-nil error. +func Zip[T any](it iter.Seq[T], errFn func() error) iter.Seq2[T, error] { + return func(yield func(T, error) bool) { + for v := range it { + if !yield(v, nil) { + break + } + } + if err := errFn(); err != nil { + var zero T + yield(zero, err) + } + } +} + +// Map returns a new iterator by applying the map function, while respecting +// the error function. Additionally, the map function can return an error if +// its own. +func Map[A, B any](i iter.Seq[A], errFn func() error, mapFn func(A) (B, error)) (iter.Seq[B], func() error) { + var retErr error + return func(yield func(B) bool) { + for v := range i { + mapped, err := mapFn(v) + if err != nil { + retErr = err + return + } + if !yield(mapped) { + return + } + } + }, func() error { + if prevErr := errFn(); prevErr != nil { + return prevErr + } + return retErr + } +} + +// Map returns a new iterator by applying the map function, while respecting +// the error function. Additionally, the map function can return an error if +// its own. +func Map2[A, B, C any](i iter.Seq[A], errFn func() error, mapFn func(A) (B, C, error)) (iter.Seq2[B, C], func() error) { + var retErr error + return func(yield func(B, C) bool) { + for v := range i { + ma, mb, err := mapFn(v) + if err != nil { + retErr = err + return + } + if !yield(ma, mb) { + return + } + } + }, func() error { + if prevErr := errFn(); prevErr != nil { + return prevErr + } + return retErr + } +} diff --git a/internal/protoutil/protoutil.go b/internal/protoutil/protoutil.go index 93bd76911..3387b4da5 100644 --- a/internal/protoutil/protoutil.go +++ b/internal/protoutil/protoutil.go @@ -1,14 +1,22 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + package protoutil import ( - "fmt" + "errors" "google.golang.org/protobuf/proto" ) +var errBufferTooSmall = errors.New("buffer too small") + func MarshalTo(buf []byte, pb proto.Message) (int, error) { if sz := proto.Size(pb); len(buf) < sz { - return 0, fmt.Errorf("buffer too small") + return 0, errBufferTooSmall } else if sz == 0 { return 0, nil } diff --git a/internal/timeutil/timeutil.go b/internal/timeutil/timeutil.go new file mode 100644 index 000000000..103de37f3 --- /dev/null +++ b/internal/timeutil/timeutil.go @@ -0,0 +1,27 @@ +// Copyright (C) 2025 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package timeutil + +import ( + "sync/atomic" + "time" +) + +var prevNanos atomic.Int64 + +// StrictlyMonotonicNanos returns the current time in Unix nanoseconds. +// Guaranteed to strictly increase for each call, regardless of the +// underlying OS timer resolution or clock jumps. +func StrictlyMonotonicNanos() int64 { + for { + old := prevNanos.Load() + now := max(time.Now().UnixNano(), old+1) + if prevNanos.CompareAndSwap(old, now) { + return now + } + } +} diff --git a/lib/api/api.go b/lib/api/api.go index 1115926cc..c7952d6af 100644 --- a/lib/api/api.go +++ b/lib/api/api.go @@ -41,10 +41,10 @@ import ( "golang.org/x/text/transform" "golang.org/x/text/unicode/norm" + "github.com/syncthing/syncthing/internal/db" "github.com/syncthing/syncthing/lib/build" "github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/connections" - "github.com/syncthing/syncthing/lib/db" "github.com/syncthing/syncthing/lib/discover" "github.com/syncthing/syncthing/lib/events" "github.com/syncthing/syncthing/lib/fs" @@ -92,7 +92,7 @@ type service struct { startupErr error listenerAddr net.Addr exitChan chan *svcutil.FatalErr - miscDB *db.NamespacedKV + miscDB *db.Typed shutdownTimeout time.Duration guiErrors logger.Recorder @@ -107,7 +107,7 @@ type Service interface { WaitForStart() error } -func New(id protocol.DeviceID, cfg config.Wrapper, assetDir, tlsDefaultCommonName string, m model.Model, defaultSub, diskSub events.BufferedSubscription, evLogger events.Logger, discoverer discover.Manager, connectionsService connections.Service, urService *ur.Service, fss model.FolderSummaryService, errors, systemLog logger.Recorder, noUpgrade bool, miscDB *db.NamespacedKV) Service { +func New(id protocol.DeviceID, cfg config.Wrapper, assetDir, tlsDefaultCommonName string, m model.Model, defaultSub, diskSub events.BufferedSubscription, evLogger events.Logger, discoverer discover.Manager, connectionsService connections.Service, urService *ur.Service, fss model.FolderSummaryService, errors, systemLog logger.Recorder, noUpgrade bool, miscDB *db.Typed) Service { return &service{ id: id, cfg: cfg, @@ -985,16 +985,11 @@ func (s *service) getDBFile(w http.ResponseWriter, r *http.Request) { http.Error(w, err.Error(), http.StatusInternalServerError) return } - mtimeMapping, mtimeErr := s.model.GetMtimeMapping(folder, file) sendJSON(w, map[string]interface{}{ "global": jsonFileInfo(gf), "local": jsonFileInfo(lf), "availability": av, - "mtime": map[string]interface{}{ - "err": mtimeErr, - "value": mtimeMapping, - }, }) } @@ -1003,28 +998,14 @@ func (s *service) getDebugFile(w http.ResponseWriter, r *http.Request) { folder := qs.Get("folder") file := qs.Get("file") - snap, err := s.model.DBSnapshot(folder) - if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) - return - } - - mtimeMapping, mtimeErr := s.model.GetMtimeMapping(folder, file) - - lf, _ := snap.Get(protocol.LocalDeviceID, file) - gf, _ := snap.GetGlobal(file) - av := snap.Availability(file) - vl := snap.DebugGlobalVersions(file) + lf, _, _ := s.model.CurrentFolderFile(folder, file) + gf, _, _ := s.model.CurrentGlobalFile(folder, file) + av, _ := s.model.Availability(folder, protocol.FileInfo{Name: file}, protocol.BlockInfo{}) sendJSON(w, map[string]interface{}{ - "global": jsonFileInfo(gf), - "local": jsonFileInfo(lf), - "availability": av, - "globalVersions": vl.String(), - "mtime": map[string]interface{}{ - "err": mtimeErr, - "value": mtimeMapping, - }, + "global": jsonFileInfo(gf), + "local": jsonFileInfo(lf), + "availability": av, }) } diff --git a/lib/api/api_auth_test.go b/lib/api/api_auth_test.go index a2d7146d9..ee51031f1 100644 --- a/lib/api/api_auth_test.go +++ b/lib/api/api_auth_test.go @@ -10,10 +10,9 @@ import ( "testing" "time" + "github.com/syncthing/syncthing/internal/db" + "github.com/syncthing/syncthing/internal/db/sqlite" "github.com/syncthing/syncthing/lib/config" - "github.com/syncthing/syncthing/lib/db" - "github.com/syncthing/syncthing/lib/db/backend" - "github.com/syncthing/syncthing/lib/events" ) var guiCfg config.GUIConfiguration @@ -131,8 +130,14 @@ func (c *mockClock) wind(t time.Duration) { func TestTokenManager(t *testing.T) { t.Parallel() - mdb, _ := db.NewLowlevel(backend.OpenMemory(), events.NoopLogger) - kdb := db.NewNamespacedKV(mdb, "test") + mdb, err := sqlite.OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + mdb.Close() + }) + kdb := db.NewMiscDB(mdb) clock := &mockClock{now: time.Now()} // Token manager keeps up to three tokens with a validity time of 24 hours. diff --git a/lib/api/api_csrf.go b/lib/api/api_csrf.go index 50c6749d8..b2e4b4eda 100644 --- a/lib/api/api_csrf.go +++ b/lib/api/api_csrf.go @@ -11,7 +11,7 @@ import ( "strings" "time" - "github.com/syncthing/syncthing/lib/db" + "github.com/syncthing/syncthing/internal/db" ) const ( @@ -34,7 +34,7 @@ type apiKeyValidator interface { // Check for CSRF token on /rest/ URLs. If a correct one is not given, reject // the request with 403. For / and /index.html, set a new CSRF cookie if none // is currently set. -func newCsrfManager(unique string, prefix string, apiKeyValidator apiKeyValidator, next http.Handler, miscDB *db.NamespacedKV) *csrfManager { +func newCsrfManager(unique string, prefix string, apiKeyValidator apiKeyValidator, next http.Handler, miscDB *db.Typed) *csrfManager { m := &csrfManager{ unique: unique, prefix: prefix, diff --git a/lib/api/api_test.go b/lib/api/api_test.go index 96ea1d1ce..bad4b456c 100644 --- a/lib/api/api_test.go +++ b/lib/api/api_test.go @@ -27,12 +27,12 @@ import ( "github.com/d4l3k/messagediff" "github.com/thejerf/suture/v4" + "github.com/syncthing/syncthing/internal/db" + "github.com/syncthing/syncthing/internal/db/sqlite" "github.com/syncthing/syncthing/lib/assets" "github.com/syncthing/syncthing/lib/build" "github.com/syncthing/syncthing/lib/config" connmocks "github.com/syncthing/syncthing/lib/connections/mocks" - "github.com/syncthing/syncthing/lib/db" - "github.com/syncthing/syncthing/lib/db/backend" discovermocks "github.com/syncthing/syncthing/lib/discover/mocks" "github.com/syncthing/syncthing/lib/events" eventmocks "github.com/syncthing/syncthing/lib/events/mocks" @@ -84,8 +84,14 @@ func TestStopAfterBrokenConfig(t *testing.T) { } w := config.Wrap("/dev/null", cfg, protocol.LocalDeviceID, events.NoopLogger) - mdb, _ := db.NewLowlevel(backend.OpenMemory(), events.NoopLogger) - kdb := db.NewMiscDataNamespace(mdb) + mdb, err := sqlite.OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + mdb.Close() + }) + kdb := db.NewMiscDB(mdb) srv := New(protocol.LocalDeviceID, w, "", "syncthing", nil, nil, nil, events.NoopLogger, nil, nil, nil, nil, nil, nil, false, kdb).(*service) srv.started = make(chan string) @@ -217,11 +223,7 @@ type httpTestCase struct { func TestAPIServiceRequests(t *testing.T) { t.Parallel() - baseURL, cancel, err := startHTTP(apiCfg) - if err != nil { - t.Fatal(err) - } - t.Cleanup(cancel) + baseURL := startHTTP(t, apiCfg) cases := []httpTestCase{ // /rest/db @@ -598,11 +600,7 @@ func TestHTTPLogin(t *testing.T) { APIKey: testAPIKey, SendBasicAuthPrompt: sendBasicAuthPrompt, }) - baseURL, cancel, err := startHTTP(cfg) - if err != nil { - t.Fatal(err) - } - t.Cleanup(cancel) + baseURL := startHTTP(t, cfg) url := baseURL + path t.Run(fmt.Sprintf("%d path", expectedOkStatus), func(t *testing.T) { @@ -795,13 +793,9 @@ func TestHTTPLogin(t *testing.T) { w := initConfig(initialPassword, t) { - baseURL, cancel, err := startHTTPWithShutdownTimeout(w, shutdownTimeout) + baseURL := startHTTPWithShutdownTimeout(t, w, shutdownTimeout) cfgPath := baseURL + "/rest/config" path := baseURL + "/meta.js" - t.Cleanup(cancel) - if err != nil { - t.Fatal(err) - } resp := httpGetBasicAuth(path, "user", initialPassword) if resp.StatusCode != http.StatusOK { @@ -813,12 +807,8 @@ func TestHTTPLogin(t *testing.T) { httpRequest(http.MethodPut, cfgPath, cfg, "", "", testAPIKey, "", "", "", nil, t) } { - baseURL, cancel, err := startHTTP(w) + baseURL := startHTTP(t, w) path := baseURL + "/meta.js" - t.Cleanup(cancel) - if err != nil { - t.Fatal(err) - } resp := httpGetBasicAuth(path, "user", initialPassword) if resp.StatusCode != http.StatusForbidden { @@ -837,13 +827,9 @@ func TestHTTPLogin(t *testing.T) { w := initConfig(initialPassword, t) { - baseURL, cancel, err := startHTTPWithShutdownTimeout(w, shutdownTimeout) + baseURL := startHTTPWithShutdownTimeout(t, w, shutdownTimeout) cfgPath := baseURL + "/rest/config/gui" path := baseURL + "/meta.js" - t.Cleanup(cancel) - if err != nil { - t.Fatal(err) - } resp := httpGetBasicAuth(path, "user", initialPassword) if resp.StatusCode != http.StatusOK { @@ -855,12 +841,8 @@ func TestHTTPLogin(t *testing.T) { httpRequest(http.MethodPut, cfgPath, cfg.GUI, "", "", testAPIKey, "", "", "", nil, t) } { - baseURL, cancel, err := startHTTP(w) + baseURL := startHTTP(t, w) path := baseURL + "/meta.js" - t.Cleanup(cancel) - if err != nil { - t.Fatal(err) - } resp := httpGetBasicAuth(path, "user", initialPassword) if resp.StatusCode != http.StatusForbidden { @@ -885,11 +867,7 @@ func TestHtmlFormLogin(t *testing.T) { Password: "$2a$10$IdIZTxTg/dCNuNEGlmLynOjqg4B1FvDKuIV5e0BB3pnWVHNb8.GSq", // bcrypt of "räksmörgås" in UTF-8 SendBasicAuthPrompt: false, }) - baseURL, cancel, err := startHTTP(cfg) - if err != nil { - t.Fatal(err) - } - t.Cleanup(cancel) + baseURL := startHTTP(t, cfg) loginUrl := baseURL + "/rest/noauth/auth/password" resourceUrl := baseURL + "/meta.js" @@ -1030,11 +1008,7 @@ func TestApiCache(t *testing.T) { RawAddress: "127.0.0.1:0", APIKey: testAPIKey, }) - baseURL, cancel, err := startHTTP(cfg) - if err != nil { - t.Fatal(err) - } - t.Cleanup(cancel) + baseURL := startHTTP(t, cfg) httpGet := func(url string, bearer string) *http.Response { return httpGet(url, "", "", "", bearer, nil, t) @@ -1059,11 +1033,11 @@ func TestApiCache(t *testing.T) { }) } -func startHTTP(cfg config.Wrapper) (string, context.CancelFunc, error) { - return startHTTPWithShutdownTimeout(cfg, 0) +func startHTTP(t *testing.T, cfg config.Wrapper) string { + return startHTTPWithShutdownTimeout(t, cfg, 0) } -func startHTTPWithShutdownTimeout(cfg config.Wrapper, shutdownTimeout time.Duration) (string, context.CancelFunc, error) { +func startHTTPWithShutdownTimeout(t *testing.T, cfg config.Wrapper, shutdownTimeout time.Duration) string { m := new(modelmocks.Model) assetDir := "../../gui" eventSub := new(eventmocks.BufferedSubscription) @@ -1086,12 +1060,18 @@ func startHTTPWithShutdownTimeout(cfg config.Wrapper, shutdownTimeout time.Durat // Instantiate the API service urService := ur.New(cfg, m, connections, false) - mdb, _ := db.NewLowlevel(backend.OpenMemory(), events.NoopLogger) - kdb := db.NewMiscDataNamespace(mdb) + mdb, err := sqlite.OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + mdb.Close() + }) + kdb := db.NewMiscDB(mdb) svc := New(protocol.LocalDeviceID, cfg, assetDir, "syncthing", m, eventSub, diskEventSub, events.NoopLogger, discoverer, connections, urService, mockedSummary, errorLog, systemLog, false, kdb).(*service) svc.started = addrChan - if shutdownTimeout > 0*time.Millisecond { + if shutdownTimeout > 0 { svc.shutdownTimeout = shutdownTimeout } @@ -1101,14 +1081,14 @@ func startHTTPWithShutdownTimeout(cfg config.Wrapper, shutdownTimeout time.Durat }) supervisor.Add(svc) ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) supervisor.ServeBackground(ctx) // Make sure the API service is listening, and get the URL to use. addr := <-addrChan tcpAddr, err := net.ResolveTCPAddr("tcp", addr) if err != nil { - cancel() - return "", cancel, fmt.Errorf("weird address from API service: %w", err) + t.Fatal(fmt.Errorf("weird address from API service: %w", err)) } host, _, _ := net.SplitHostPort(cfg.GUI().RawAddress) @@ -1117,17 +1097,13 @@ func startHTTPWithShutdownTimeout(cfg config.Wrapper, shutdownTimeout time.Durat } baseURL := fmt.Sprintf("http://%s", net.JoinHostPort(host, strconv.Itoa(tcpAddr.Port))) - return baseURL, cancel, nil + return baseURL } func TestCSRFRequired(t *testing.T) { t.Parallel() - baseURL, cancel, err := startHTTP(apiCfg) - if err != nil { - t.Fatal("Unexpected error from getting base URL:", err) - } - t.Cleanup(cancel) + baseURL := startHTTP(t, apiCfg) cli := &http.Client{ Timeout: time.Minute, @@ -1245,11 +1221,7 @@ func TestCSRFRequired(t *testing.T) { func TestRandomString(t *testing.T) { t.Parallel() - baseURL, cancel, err := startHTTP(apiCfg) - if err != nil { - t.Fatal(err) - } - defer cancel() + baseURL := startHTTP(t, apiCfg) cli := &http.Client{ Timeout: time.Second, } @@ -1304,7 +1276,7 @@ func TestConfigPostOK(t *testing.T) { ] }`)) - resp, err := testConfigPost(cfg) + resp, err := testConfigPost(t, cfg) if err != nil { t.Fatal(err) } @@ -1325,7 +1297,7 @@ func TestConfigPostDupFolder(t *testing.T) { ] }`)) - resp, err := testConfigPost(cfg) + resp, err := testConfigPost(t, cfg) if err != nil { t.Fatal(err) } @@ -1334,12 +1306,10 @@ func TestConfigPostDupFolder(t *testing.T) { } } -func testConfigPost(data io.Reader) (*http.Response, error) { - baseURL, cancel, err := startHTTP(apiCfg) - if err != nil { - return nil, err - } - defer cancel() +func testConfigPost(t *testing.T, data io.Reader) (*http.Response, error) { + t.Helper() + + baseURL := startHTTP(t, apiCfg) cli := &http.Client{ Timeout: time.Second, } @@ -1356,11 +1326,7 @@ func TestHostCheck(t *testing.T) { cfg := newMockedConfig() cfg.GUIReturns(config.GUIConfiguration{RawAddress: "127.0.0.1:0"}) - baseURL, cancel, err := startHTTP(cfg) - if err != nil { - t.Fatal(err) - } - defer cancel() + baseURL := startHTTP(t, cfg) // A normal HTTP get to the localhost-bound service should succeed @@ -1419,11 +1385,7 @@ func TestHostCheck(t *testing.T) { RawAddress: "127.0.0.1:0", InsecureSkipHostCheck: true, }) - baseURL, cancel, err = startHTTP(cfg) - if err != nil { - t.Fatal(err) - } - defer cancel() + baseURL = startHTTP(t, cfg) // A request with a suspicious Host header should be allowed @@ -1445,11 +1407,7 @@ func TestHostCheck(t *testing.T) { cfg.GUIReturns(config.GUIConfiguration{ RawAddress: "0.0.0.0:0", }) - baseURL, cancel, err = startHTTP(cfg) - if err != nil { - t.Fatal(err) - } - defer cancel() + baseURL = startHTTP(t, cfg) // A request with a suspicious Host header should be allowed @@ -1476,11 +1434,7 @@ func TestHostCheck(t *testing.T) { cfg.GUIReturns(config.GUIConfiguration{ RawAddress: "[::1]:0", }) - baseURL, cancel, err = startHTTP(cfg) - if err != nil { - t.Fatal(err) - } - defer cancel() + baseURL = startHTTP(t, cfg) // A normal HTTP get to the localhost-bound service should succeed @@ -1568,11 +1522,7 @@ func TestAddressIsLocalhost(t *testing.T) { func TestAccessControlAllowOriginHeader(t *testing.T) { t.Parallel() - baseURL, cancel, err := startHTTP(apiCfg) - if err != nil { - t.Fatal(err) - } - defer cancel() + baseURL := startHTTP(t, apiCfg) cli := &http.Client{ Timeout: time.Second, } @@ -1596,11 +1546,7 @@ func TestAccessControlAllowOriginHeader(t *testing.T) { func TestOptionsRequest(t *testing.T) { t.Parallel() - baseURL, cancel, err := startHTTP(apiCfg) - if err != nil { - t.Fatal(err) - } - defer cancel() + baseURL := startHTTP(t, apiCfg) cli := &http.Client{ Timeout: time.Second, } @@ -1632,8 +1578,14 @@ func TestEventMasks(t *testing.T) { cfg := newMockedConfig() defSub := new(eventmocks.BufferedSubscription) diskSub := new(eventmocks.BufferedSubscription) - mdb, _ := db.NewLowlevel(backend.OpenMemory(), events.NoopLogger) - kdb := db.NewMiscDataNamespace(mdb) + mdb, err := sqlite.OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + mdb.Close() + }) + kdb := db.NewMiscDB(mdb) svc := New(protocol.LocalDeviceID, cfg, "", "syncthing", nil, defSub, diskSub, events.NoopLogger, nil, nil, nil, nil, nil, nil, false, kdb).(*service) if mask := svc.getEventMask(""); mask != DefaultEventMask { @@ -1780,11 +1732,7 @@ func TestConfigChanges(t *testing.T) { cfgCtx, cfgCancel := context.WithCancel(context.Background()) go w.Serve(cfgCtx) defer cfgCancel() - baseURL, cancel, err := startHTTP(w) - if err != nil { - t.Fatal("Unexpected error from getting base URL:", err) - } - defer cancel() + baseURL := startHTTP(t, w) cli := &http.Client{ Timeout: time.Minute, diff --git a/lib/api/testdata/config/config.xml b/lib/api/testdata/config/config.xml index 5bd47cc87..3f547e0e9 100644 --- a/lib/api/testdata/config/config.xml +++ b/lib/api/testdata/config/config.xml @@ -18,7 +18,6 @@ false false false - 25 .stfolder true @@ -39,7 +38,6 @@ false false false - 25 .stfolder true diff --git a/lib/api/tokenmanager.go b/lib/api/tokenmanager.go index 77c57a581..ddf6b8d28 100644 --- a/lib/api/tokenmanager.go +++ b/lib/api/tokenmanager.go @@ -14,9 +14,9 @@ import ( "google.golang.org/protobuf/proto" + "github.com/syncthing/syncthing/internal/db" "github.com/syncthing/syncthing/internal/gen/apiproto" "github.com/syncthing/syncthing/lib/config" - "github.com/syncthing/syncthing/lib/db" "github.com/syncthing/syncthing/lib/events" "github.com/syncthing/syncthing/lib/rand" "github.com/syncthing/syncthing/lib/sync" @@ -24,7 +24,7 @@ import ( type tokenManager struct { key string - miscDB *db.NamespacedKV + miscDB *db.Typed lifetime time.Duration maxItems int @@ -35,7 +35,7 @@ type tokenManager struct { saveTimer *time.Timer } -func newTokenManager(key string, miscDB *db.NamespacedKV, lifetime time.Duration, maxItems int) *tokenManager { +func newTokenManager(key string, miscDB *db.Typed, lifetime time.Duration, maxItems int) *tokenManager { var tokens apiproto.TokenSet if bs, ok, _ := miscDB.Bytes(key); ok { _ = proto.Unmarshal(bs, &tokens) // best effort @@ -152,7 +152,7 @@ type tokenCookieManager struct { tokens *tokenManager } -func newTokenCookieManager(shortID string, guiCfg config.GUIConfiguration, evLogger events.Logger, miscDB *db.NamespacedKV) *tokenCookieManager { +func newTokenCookieManager(shortID string, guiCfg config.GUIConfiguration, evLogger events.Logger, miscDB *db.Typed) *tokenCookieManager { return &tokenCookieManager{ cookieName: "sessionid-" + shortID, shortID: shortID, diff --git a/lib/build/build.go b/lib/build/build.go index a07682451..32ee763f4 100644 --- a/lib/build/build.go +++ b/lib/build/build.go @@ -18,7 +18,7 @@ import ( "time" ) -const Codename = "Gold Grasshopper" +const Codename = "Hafnium Hornet" var ( // Injected by build script @@ -28,6 +28,9 @@ var ( Stamp = "0" Tags = "" + // Added to by other packages + extraTags []string + // Set by init() Date time.Time IsRelease bool @@ -43,6 +46,12 @@ var ( "STNORESTART", "STNOUPGRADE", } + replaceTags = map[string]string{ + "sqlite_omit_load_extension": "", + "sqlite_dbstat": "", + "osusergo": "", + "netgo": "", + } ) const versionExtraAllowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-. " @@ -108,8 +117,23 @@ func TagsList() []string { if Extra != "" { tags = append(tags, Extra) } + tags = append(tags, extraTags...) + + // Replace any tag values we want to have more user friendly versions, + // or be removed + for i, tag := range tags { + if repl, ok := replaceTags[tag]; ok { + tags[i] = repl + } + } slices.Sort(tags) + + // Remove any empty tags, which will be at the front of the list now + for len(tags) > 0 && tags[0] == "" { + tags = tags[1:] + } + return tags } @@ -124,3 +148,8 @@ func filterString(s, allowedChars string) string { } return res.String() } + +func AddTag(tag string) { + extraTags = append(extraTags, tag) + LongVersion = LongVersionFor("syncthing") +} diff --git a/lib/config/config.go b/lib/config/config.go index 78951be89..08feb4aa1 100644 --- a/lib/config/config.go +++ b/lib/config/config.go @@ -31,7 +31,7 @@ import ( const ( OldestHandledVersion = 10 - CurrentVersion = 37 + CurrentVersion = 50 MaxRescanIntervalS = 365 * 24 * 60 * 60 ) diff --git a/lib/config/config_test.go b/lib/config/config_test.go index 8031fadb9..1ae4404fa 100644 --- a/lib/config/config_test.go +++ b/lib/config/config_test.go @@ -110,6 +110,7 @@ func TestDefaultValues(t *testing.T) { FSWatcherEnabled: true, FSWatcherDelayS: 10, IgnorePerms: false, + PullerDelayS: 1, AutoNormalize: true, MinDiskFree: size, Versioning: VersioningConfiguration{ @@ -117,10 +118,9 @@ func TestDefaultValues(t *testing.T) { CleanupIntervalS: 3600, Params: map[string]string{}, }, - MaxConflicts: 10, - WeakHashThresholdPct: 25, - MarkerName: ".stfolder", - MaxConcurrentWrites: 2, + MaxConflicts: 10, + MarkerName: ".stfolder", + MaxConcurrentWrites: 2, XattrFilter: XattrFilter{ Entries: []XattrFilterEntry{}, MaxSingleEntrySize: 1024, @@ -187,6 +187,7 @@ func TestDeviceConfig(t *testing.T) { FSWatcherDelayS: 10, Copiers: 0, Hashers: 0, + PullerDelayS: 1, AutoNormalize: true, MinDiskFree: Size{1, "%"}, MaxConflicts: -1, @@ -195,10 +196,9 @@ func TestDeviceConfig(t *testing.T) { FSType: FilesystemTypeBasic, Params: map[string]string{}, }, - WeakHashThresholdPct: 25, - MarkerName: DefaultMarkerName, - JunctionsAsDirs: true, - MaxConcurrentWrites: maxConcurrentWritesDefault, + MarkerName: DefaultMarkerName, + JunctionsAsDirs: true, + MaxConcurrentWrites: maxConcurrentWritesDefault, XattrFilter: XattrFilter{ MaxSingleEntrySize: 1024, MaxTotalSize: 4096, @@ -495,7 +495,7 @@ func TestIssue1262(t *testing.T) { t.Fatal(err) } - actual := cfg.Folders()["test"].Filesystem(nil).URI() + actual := cfg.Folders()["test"].Filesystem().URI() expected := `e:\` if actual != expected { @@ -533,7 +533,7 @@ func TestFolderPath(t *testing.T) { Path: "~/tmp", } - realPath := folder.Filesystem(nil).URI() + realPath := folder.Filesystem().URI() if !filepath.IsAbs(realPath) { t.Error(realPath, "should be absolute") } diff --git a/lib/config/deviceconfiguration.go b/lib/config/deviceconfiguration.go index b12514175..28a51fe9b 100644 --- a/lib/config/deviceconfiguration.go +++ b/lib/config/deviceconfiguration.go @@ -13,7 +13,7 @@ import ( "github.com/syncthing/syncthing/lib/protocol" ) -const defaultNumConnections = 1 // number of connections to use by default; may change in the future. +const defaultNumConnections = 3 // number of connections to use by default type DeviceConfiguration struct { DeviceID protocol.DeviceID `json:"deviceID" xml:"id,attr" nodefault:"true"` diff --git a/lib/config/folderconfiguration.go b/lib/config/folderconfiguration.go index 0d23b1397..1dee2085e 100644 --- a/lib/config/folderconfiguration.go +++ b/lib/config/folderconfiguration.go @@ -22,7 +22,6 @@ import ( "github.com/shirou/gopsutil/v4/disk" "github.com/syncthing/syncthing/lib/build" - "github.com/syncthing/syncthing/lib/db" "github.com/syncthing/syncthing/lib/fs" "github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/structutil" @@ -69,11 +68,11 @@ type FolderConfiguration struct { IgnoreDelete bool `json:"ignoreDelete" xml:"ignoreDelete"` ScanProgressIntervalS int `json:"scanProgressIntervalS" xml:"scanProgressIntervalS"` PullerPauseS int `json:"pullerPauseS" xml:"pullerPauseS"` + PullerDelayS float64 `json:"pullerDelayS" xml:"pullerDelayS" default:"1"` MaxConflicts int `json:"maxConflicts" xml:"maxConflicts" default:"10"` DisableSparseFiles bool `json:"disableSparseFiles" xml:"disableSparseFiles"` DisableTempIndexes bool `json:"disableTempIndexes" xml:"disableTempIndexes"` Paused bool `json:"paused" xml:"paused"` - WeakHashThresholdPct int `json:"weakHashThresholdPct" xml:"weakHashThresholdPct"` MarkerName string `json:"markerName" xml:"markerName"` CopyOwnershipFromParent bool `json:"copyOwnershipFromParent" xml:"copyOwnershipFromParent"` RawModTimeWindowS int `json:"modTimeWindowS" xml:"modTimeWindowS"` @@ -123,26 +122,24 @@ func (f FolderConfiguration) Copy() FolderConfiguration { // Filesystem creates a filesystem for the path and options of this folder. // The fset parameter may be nil, in which case no mtime handling on top of // the filesystem is provided. -func (f FolderConfiguration) Filesystem(fset *db.FileSet) fs.Filesystem { +func (f FolderConfiguration) Filesystem(extraOpts ...fs.Option) fs.Filesystem { // This is intentionally not a pointer method, because things like // cfg.Folders["default"].Filesystem(nil) should be valid. - opts := make([]fs.Option, 0, 3) + var opts []fs.Option if f.FilesystemType == FilesystemTypeBasic && f.JunctionsAsDirs { opts = append(opts, new(fs.OptionJunctionsAsDirs)) } if !f.CaseSensitiveFS { opts = append(opts, new(fs.OptionDetectCaseConflicts)) } - if fset != nil { - opts = append(opts, fset.MtimeOption()) - } + opts = append(opts, extraOpts...) return fs.NewFilesystem(f.FilesystemType.ToFS(), f.Path, opts...) } func (f FolderConfiguration) ModTimeWindow() time.Duration { dur := time.Duration(f.RawModTimeWindowS) * time.Second if f.RawModTimeWindowS < 1 && build.IsAndroid { - if usage, err := disk.Usage(f.Filesystem(nil).URI()); err != nil { + if usage, err := disk.Usage(f.Filesystem().URI()); err != nil { dur = 2 * time.Second l.Debugf(`Detecting FS at "%v" on android: Setting mtime window to 2s: err == "%v"`, f.Path, err) } else if strings.HasPrefix(strings.ToLower(usage.Fstype), "ext2") || strings.HasPrefix(strings.ToLower(usage.Fstype), "ext3") || strings.HasPrefix(strings.ToLower(usage.Fstype), "ext4") { @@ -166,7 +163,7 @@ func (f *FolderConfiguration) CreateMarker() error { return nil } - ffs := f.Filesystem(nil) + ffs := f.Filesystem() // Create the marker as a directory err := ffs.Mkdir(DefaultMarkerName, 0o755) @@ -193,7 +190,7 @@ func (f *FolderConfiguration) CreateMarker() error { } func (f *FolderConfiguration) RemoveMarker() error { - ffs := f.Filesystem(nil) + ffs := f.Filesystem() _ = ffs.Remove(filepath.Join(DefaultMarkerName, f.markerFilename())) return ffs.Remove(DefaultMarkerName) } @@ -213,7 +210,7 @@ func (f *FolderConfiguration) markerContents() []byte { // CheckPath returns nil if the folder root exists and contains the marker file func (f *FolderConfiguration) CheckPath() error { - return f.checkFilesystemPath(f.Filesystem(nil), ".") + return f.checkFilesystemPath(f.Filesystem(), ".") } func (f *FolderConfiguration) checkFilesystemPath(ffs fs.Filesystem, path string) error { @@ -256,7 +253,7 @@ func (f *FolderConfiguration) CreateRoot() (err error) { permBits = 0o700 } - filesystem := f.Filesystem(nil) + filesystem := f.Filesystem() if _, err = filesystem.Stat("."); fs.IsNotExist(err) { err = filesystem.MkdirAll(".", permBits) @@ -314,10 +311,6 @@ func (f *FolderConfiguration) prepare(myID protocol.DeviceID, existingDevices ma f.Versioning.CleanupIntervalS = 0 } - if f.WeakHashThresholdPct == 0 { - f.WeakHashThresholdPct = 25 - } - if f.MarkerName == "" { f.MarkerName = DefaultMarkerName } @@ -371,7 +364,7 @@ func (f *FolderConfiguration) CheckAvailableSpace(req uint64) error { if val <= 0 { return nil } - fs := f.Filesystem(nil) + fs := f.Filesystem() usage, err := fs.Usage(".") if err != nil { return nil //nolint: nilerr diff --git a/lib/config/migrations.go b/lib/config/migrations.go index 838d0154f..532b3012c 100644 --- a/lib/config/migrations.go +++ b/lib/config/migrations.go @@ -28,6 +28,7 @@ import ( // put the newest on top for readability. var ( migrations = migrationSet{ + {50, migrateToConfigV50}, {37, migrateToConfigV37}, {36, migrateToConfigV36}, {35, migrateToConfigV35}, @@ -97,6 +98,10 @@ func (m migration) apply(cfg *Configuration) { cfg.Version = m.targetVersion } +func migrateToConfigV50(cfg *Configuration) { + // v50 is Syncthing 2.0 +} + func migrateToConfigV37(cfg *Configuration) { // "scan ownership" changed name to "send ownership" for i := range cfg.Folders { @@ -209,7 +214,7 @@ func migrateToConfigV23(cfg *Configuration) { // marker name in later versions. for i := range cfg.Folders { - fs := cfg.Folders[i].Filesystem(nil) + fs := cfg.Folders[i].Filesystem() // Invalid config posted, or tests. if fs == nil { continue @@ -245,18 +250,18 @@ func migrateToConfigV21(cfg *Configuration) { switch folder.Versioning.Type { case "simple", "trashcan": // Clean out symlinks in the known place - cleanSymlinks(folder.Filesystem(nil), ".stversions") + cleanSymlinks(folder.Filesystem(), ".stversions") case "staggered": versionDir := folder.Versioning.Params["versionsPath"] if versionDir == "" { // default place - cleanSymlinks(folder.Filesystem(nil), ".stversions") + cleanSymlinks(folder.Filesystem(), ".stversions") } else if filepath.IsAbs(versionDir) { // absolute cleanSymlinks(fs.NewFilesystem(fs.FilesystemTypeBasic, versionDir), ".") } else { // relative to folder - cleanSymlinks(folder.Filesystem(nil), versionDir) + cleanSymlinks(folder.Filesystem(), versionDir) } } } diff --git a/lib/config/optionsconfiguration.go b/lib/config/optionsconfiguration.go index 636c6be81..173a31e27 100644 --- a/lib/config/optionsconfiguration.go +++ b/lib/config/optionsconfiguration.go @@ -63,7 +63,6 @@ type OptionsConfiguration struct { StunKeepaliveStartS int `json:"stunKeepaliveStartS" xml:"stunKeepaliveStartS" default:"180"` StunKeepaliveMinS int `json:"stunKeepaliveMinS" xml:"stunKeepaliveMinS" default:"20"` RawStunServers []string `json:"stunServers" xml:"stunServer" default:"default"` - DatabaseTuning Tuning `json:"databaseTuning" xml:"databaseTuning" restart:"true"` RawMaxCIRequestKiB int `json:"maxConcurrentIncomingRequestKiB" xml:"maxConcurrentIncomingRequestKiB"` AnnounceLANAddresses bool `json:"announceLANAddresses" xml:"announceLANAddresses" default:"true"` SendFullIndexOnUpgrade bool `json:"sendFullIndexOnUpgrade" xml:"sendFullIndexOnUpgrade"` diff --git a/lib/config/testdata/overridenvalues.xml b/lib/config/testdata/overridenvalues.xml index eb372aada..0fb3a14b2 100644 --- a/lib/config/testdata/overridenvalues.xml +++ b/lib/config/testdata/overridenvalues.xml @@ -72,7 +72,6 @@ false false false - 25 .stfolder false 0 diff --git a/lib/config/tuning.go b/lib/config/tuning.go deleted file mode 100644 index c2fa9df43..000000000 --- a/lib/config/tuning.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (C) 2019 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package config - -type Tuning int32 - -const ( - TuningAuto Tuning = 0 - TuningSmall Tuning = 1 - TuningLarge Tuning = 2 -) - -func (t Tuning) String() string { - switch t { - case TuningAuto: - return "auto" - case TuningSmall: - return "small" - case TuningLarge: - return "large" - default: - return "unknown" - } -} - -func (t Tuning) MarshalText() ([]byte, error) { - return []byte(t.String()), nil -} - -func (t *Tuning) UnmarshalText(bs []byte) error { - switch string(bs) { - case "auto": - *t = TuningAuto - case "small": - *t = TuningSmall - case "large": - *t = TuningLarge - default: - *t = TuningAuto - } - return nil -} diff --git a/lib/config/tuning_test.go b/lib/config/tuning_test.go deleted file mode 100644 index d913492ff..000000000 --- a/lib/config/tuning_test.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2019 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package config_test - -import ( - "testing" - - "github.com/syncthing/syncthing/lib/config" - "github.com/syncthing/syncthing/lib/db/backend" -) - -func TestTuningMatches(t *testing.T) { - if int(config.TuningAuto) != int(backend.TuningAuto) { - t.Error("mismatch for TuningAuto") - } - if int(config.TuningSmall) != int(backend.TuningSmall) { - t.Error("mismatch for TuningSmall") - } - if int(config.TuningLarge) != int(backend.TuningLarge) { - t.Error("mismatch for TuningLarge") - } -} diff --git a/lib/db/.gitignore b/lib/db/.gitignore deleted file mode 100644 index d5316784f..000000000 --- a/lib/db/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -!*.zip -testdata/*.db diff --git a/lib/db/backend/backend_test.go b/lib/db/backend/backend_test.go deleted file mode 100644 index d755e0cb6..000000000 --- a/lib/db/backend/backend_test.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (C) 2019 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package backend - -import "testing" - -// testBackendBehavior is the generic test suite that must be fulfilled by -// every backend implementation. It should be called by each implementation -// as (part of) their test suite. -func testBackendBehavior(t *testing.T, open func() Backend) { - t.Run("WriteIsolation", func(t *testing.T) { testWriteIsolation(t, open) }) - t.Run("DeleteNonexisten", func(t *testing.T) { testDeleteNonexistent(t, open) }) - t.Run("IteratorClosedDB", func(t *testing.T) { testIteratorClosedDB(t, open) }) -} - -func testWriteIsolation(t *testing.T, open func() Backend) { - // Values written during a transaction should not be read back, our - // updateGlobal depends on this. - - db := open() - defer db.Close() - - // Sanity check - _ = db.Put([]byte("a"), []byte("a")) - v, _ := db.Get([]byte("a")) - if string(v) != "a" { - t.Fatal("read back should work") - } - - // Now in a transaction we should still see the old value - tx, _ := db.NewWriteTransaction() - defer tx.Release() - _ = tx.Put([]byte("a"), []byte("b")) - v, _ = tx.Get([]byte("a")) - if string(v) != "a" { - t.Fatal("read in transaction should read the old value") - } -} - -func testDeleteNonexistent(t *testing.T, open func() Backend) { - // Deleting a non-existent key is not an error - - db := open() - defer db.Close() - - err := db.Delete([]byte("a")) - if err != nil { - t.Error(err) - } -} - -// Either creating the iterator or the .Error() method of the returned iterator -// should return an error and IsClosed(err) == true. -func testIteratorClosedDB(t *testing.T, open func() Backend) { - db := open() - - _ = db.Put([]byte("a"), []byte("a")) - - db.Close() - - it, err := db.NewPrefixIterator(nil) - if err != nil { - if !IsClosed(err) { - t.Error("NewPrefixIterator: IsClosed(err) == false:", err) - } - return - } - it.Next() - if err := it.Error(); !IsClosed(err) { - t.Error("Next: IsClosed(err) == false:", err) - } -} diff --git a/lib/db/backend/debug.go b/lib/db/backend/debug.go deleted file mode 100644 index 9a86fcefa..000000000 --- a/lib/db/backend/debug.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (C) 2019 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package backend - -import ( - "github.com/syncthing/syncthing/lib/logger" -) - -var l = logger.DefaultLogger.NewFacility("backend", "The database backend") diff --git a/lib/db/backend/leveldb_backend.go b/lib/db/backend/leveldb_backend.go deleted file mode 100644 index 577ff4b82..000000000 --- a/lib/db/backend/leveldb_backend.go +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright (C) 2018 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package backend - -import ( - "github.com/syndtr/goleveldb/leveldb" - "github.com/syndtr/goleveldb/leveldb/iterator" - "github.com/syndtr/goleveldb/leveldb/util" -) - -const ( - // Never flush transactions smaller than this, even on Checkpoint(). - // This just needs to be just large enough to avoid flushing - // transactions when they are super tiny, thus creating millions of tiny - // transactions unnecessarily. - dbFlushBatchMin = 64 << KiB - // Once a transaction reaches this size, flush it unconditionally. This - // should be large enough to avoid forcing a flush between Checkpoint() - // calls in loops where we do those, so in principle just large enough - // to hold a FileInfo plus corresponding version list and metadata - // updates or two. - dbFlushBatchMax = 1 << MiB -) - -// leveldbBackend implements Backend on top of a leveldb -type leveldbBackend struct { - ldb *leveldb.DB - closeWG *closeWaitGroup - location string -} - -func newLeveldbBackend(ldb *leveldb.DB, location string) *leveldbBackend { - return &leveldbBackend{ - ldb: ldb, - closeWG: &closeWaitGroup{}, - location: location, - } -} - -func (b *leveldbBackend) NewReadTransaction() (ReadTransaction, error) { - return b.newSnapshot() -} - -func (b *leveldbBackend) newSnapshot() (leveldbSnapshot, error) { - rel, err := newReleaser(b.closeWG) - if err != nil { - return leveldbSnapshot{}, err - } - snap, err := b.ldb.GetSnapshot() - if err != nil { - rel.Release() - return leveldbSnapshot{}, wrapLeveldbErr(err) - } - return leveldbSnapshot{ - snap: snap, - rel: rel, - }, nil -} - -func (b *leveldbBackend) NewWriteTransaction(hooks ...CommitHook) (WriteTransaction, error) { - rel, err := newReleaser(b.closeWG) - if err != nil { - return nil, err - } - snap, err := b.newSnapshot() - if err != nil { - rel.Release() - return nil, err // already wrapped - } - return &leveldbTransaction{ - leveldbSnapshot: snap, - ldb: b.ldb, - batch: new(leveldb.Batch), - rel: rel, - commitHooks: hooks, - inFlush: false, - }, nil -} - -func (b *leveldbBackend) Close() error { - b.closeWG.CloseWait() - return wrapLeveldbErr(b.ldb.Close()) -} - -func (b *leveldbBackend) Get(key []byte) ([]byte, error) { - val, err := b.ldb.Get(key, nil) - return val, wrapLeveldbErr(err) -} - -func (b *leveldbBackend) NewPrefixIterator(prefix []byte) (Iterator, error) { - return &leveldbIterator{b.ldb.NewIterator(util.BytesPrefix(prefix), nil)}, nil -} - -func (b *leveldbBackend) NewRangeIterator(first, last []byte) (Iterator, error) { - return &leveldbIterator{b.ldb.NewIterator(&util.Range{Start: first, Limit: last}, nil)}, nil -} - -func (b *leveldbBackend) Put(key, val []byte) error { - return wrapLeveldbErr(b.ldb.Put(key, val, nil)) -} - -func (b *leveldbBackend) Delete(key []byte) error { - return wrapLeveldbErr(b.ldb.Delete(key, nil)) -} - -func (b *leveldbBackend) Compact() error { - // Race is detected during testing when db is closed while compaction - // is ongoing. - err := b.closeWG.Add(1) - if err != nil { - return err - } - defer b.closeWG.Done() - return wrapLeveldbErr(b.ldb.CompactRange(util.Range{})) -} - -func (b *leveldbBackend) Location() string { - return b.location -} - -// leveldbSnapshot implements backend.ReadTransaction -type leveldbSnapshot struct { - snap *leveldb.Snapshot - rel *releaser -} - -func (l leveldbSnapshot) Get(key []byte) ([]byte, error) { - val, err := l.snap.Get(key, nil) - return val, wrapLeveldbErr(err) -} - -func (l leveldbSnapshot) NewPrefixIterator(prefix []byte) (Iterator, error) { - return l.snap.NewIterator(util.BytesPrefix(prefix), nil), nil -} - -func (l leveldbSnapshot) NewRangeIterator(first, last []byte) (Iterator, error) { - return l.snap.NewIterator(&util.Range{Start: first, Limit: last}, nil), nil -} - -func (l leveldbSnapshot) Release() { - l.snap.Release() - l.rel.Release() -} - -// leveldbTransaction implements backend.WriteTransaction using a batch (not -// an actual leveldb transaction) -type leveldbTransaction struct { - leveldbSnapshot - ldb *leveldb.DB - batch *leveldb.Batch - rel *releaser - commitHooks []CommitHook - inFlush bool -} - -func (t *leveldbTransaction) Delete(key []byte) error { - t.batch.Delete(key) - return t.checkFlush(dbFlushBatchMax) -} - -func (t *leveldbTransaction) Put(key, val []byte) error { - t.batch.Put(key, val) - return t.checkFlush(dbFlushBatchMax) -} - -func (t *leveldbTransaction) Checkpoint() error { - return t.checkFlush(dbFlushBatchMin) -} - -func (t *leveldbTransaction) Commit() error { - err := wrapLeveldbErr(t.flush()) - t.leveldbSnapshot.Release() - t.rel.Release() - return err -} - -func (t *leveldbTransaction) Release() { - t.leveldbSnapshot.Release() - t.rel.Release() -} - -// checkFlush flushes and resets the batch if its size exceeds the given size. -func (t *leveldbTransaction) checkFlush(size int) error { - // Hooks might put values in the database, which triggers a checkFlush which might trigger a flush, - // which might trigger the hooks. - // Don't recurse... - if t.inFlush || len(t.batch.Dump()) < size { - return nil - } - return t.flush() -} - -func (t *leveldbTransaction) flush() error { - t.inFlush = true - defer func() { t.inFlush = false }() - - for _, hook := range t.commitHooks { - if err := hook(t); err != nil { - return err - } - } - if t.batch.Len() == 0 { - return nil - } - if err := t.ldb.Write(t.batch, nil); err != nil { - return wrapLeveldbErr(err) - } - t.batch.Reset() - return nil -} - -type leveldbIterator struct { - iterator.Iterator -} - -func (it *leveldbIterator) Error() error { - return wrapLeveldbErr(it.Iterator.Error()) -} - -// wrapLeveldbErr wraps errors so that the backend package can recognize them -func wrapLeveldbErr(err error) error { - switch err { - case leveldb.ErrClosed: - return errClosed - case leveldb.ErrNotFound: - return errNotFound - } - return err -} diff --git a/lib/db/backend/leveldb_open.go b/lib/db/backend/leveldb_open.go deleted file mode 100644 index 3908d7569..000000000 --- a/lib/db/backend/leveldb_open.go +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright (C) 2018 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package backend - -import ( - "fmt" - "os" - "strconv" - "strings" - - "github.com/syndtr/goleveldb/leveldb" - "github.com/syndtr/goleveldb/leveldb/errors" - "github.com/syndtr/goleveldb/leveldb/opt" - "github.com/syndtr/goleveldb/leveldb/storage" - "github.com/syndtr/goleveldb/leveldb/util" -) - -const ( - dbMaxOpenFiles = 100 - - // A large database is > 200 MiB. It's a mostly arbitrary value, but - // it's also the case that each file is 2 MiB by default and when we - // have dbMaxOpenFiles of them we will need to start thrashing fd:s. - // Switching to large database settings causes larger files to be used - // when compacting, reducing the number. - dbLargeThreshold = dbMaxOpenFiles * (2 << MiB) - - KiB = 10 - MiB = 20 -) - -// OpenLevelDB attempts to open the database at the given location, and runs -// recovery on it if opening fails. Worst case, if recovery is not possible, -// the database is erased and created from scratch. -func OpenLevelDB(location string, tuning Tuning) (Backend, error) { - opts := optsFor(location, tuning) - ldb, err := open(location, opts) - if err != nil { - return nil, err - } - return newLeveldbBackend(ldb, location), nil -} - -// OpenLevelDBAuto is OpenLevelDB with TuningAuto tuning. -func OpenLevelDBAuto(location string) (Backend, error) { - return OpenLevelDB(location, TuningAuto) -} - -// OpenLevelDBRO attempts to open the database at the given location, read -// only. -func OpenLevelDBRO(location string) (Backend, error) { - opts := &opt.Options{ - OpenFilesCacheCapacity: dbMaxOpenFiles, - ReadOnly: true, - } - ldb, err := open(location, opts) - if err != nil { - return nil, err - } - return newLeveldbBackend(ldb, location), nil -} - -// OpenLevelDBMemory returns a new Backend referencing an in-memory database. -func OpenLevelDBMemory() Backend { - ldb, _ := leveldb.Open(storage.NewMemStorage(), nil) - return newLeveldbBackend(ldb, "") -} - -// optsFor returns the database options to use when opening a database with -// the given location and tuning. Settings can be overridden by debug -// environment variables. -func optsFor(location string, tuning Tuning) *opt.Options { - large := false - switch tuning { - case TuningLarge: - large = true - case TuningAuto: - large = dbIsLarge(location) - } - - var ( - // Set defaults used for small databases. - defaultBlockCacheCapacity = 0 // 0 means let leveldb use default - defaultBlockSize = 0 - defaultCompactionTableSize = 0 - defaultCompactionTableSizeMultiplier = 0 - defaultWriteBuffer = 16 << MiB // increased from leveldb default of 4 MiB - defaultCompactionL0Trigger = opt.DefaultCompactionL0Trigger // explicit because we use it as base for other stuff - ) - - if large { - // Change the parameters for better throughput at the price of some - // RAM and larger files. This results in larger batches of writes - // and compaction at a lower frequency. - l.Infoln("Using large-database tuning") - - defaultBlockCacheCapacity = 64 << MiB - defaultBlockSize = 64 << KiB - defaultCompactionTableSize = 16 << MiB - defaultCompactionTableSizeMultiplier = 20 // 2.0 after division by ten - defaultWriteBuffer = 64 << MiB - defaultCompactionL0Trigger = 8 // number of l0 files - } - - opts := &opt.Options{ - BlockCacheCapacity: debugEnvValue("BlockCacheCapacity", defaultBlockCacheCapacity), - BlockCacheEvictRemoved: debugEnvValue("BlockCacheEvictRemoved", 0) != 0, - BlockRestartInterval: debugEnvValue("BlockRestartInterval", 0), - BlockSize: debugEnvValue("BlockSize", defaultBlockSize), - CompactionExpandLimitFactor: debugEnvValue("CompactionExpandLimitFactor", 0), - CompactionGPOverlapsFactor: debugEnvValue("CompactionGPOverlapsFactor", 0), - CompactionL0Trigger: debugEnvValue("CompactionL0Trigger", defaultCompactionL0Trigger), - CompactionSourceLimitFactor: debugEnvValue("CompactionSourceLimitFactor", 0), - CompactionTableSize: debugEnvValue("CompactionTableSize", defaultCompactionTableSize), - CompactionTableSizeMultiplier: float64(debugEnvValue("CompactionTableSizeMultiplier", defaultCompactionTableSizeMultiplier)) / 10.0, - CompactionTotalSize: debugEnvValue("CompactionTotalSize", 0), - CompactionTotalSizeMultiplier: float64(debugEnvValue("CompactionTotalSizeMultiplier", 0)) / 10.0, - DisableBufferPool: debugEnvValue("DisableBufferPool", 0) != 0, - DisableBlockCache: debugEnvValue("DisableBlockCache", 0) != 0, - DisableCompactionBackoff: debugEnvValue("DisableCompactionBackoff", 0) != 0, - DisableLargeBatchTransaction: debugEnvValue("DisableLargeBatchTransaction", 0) != 0, - NoSync: debugEnvValue("NoSync", 0) != 0, - NoWriteMerge: debugEnvValue("NoWriteMerge", 0) != 0, - OpenFilesCacheCapacity: debugEnvValue("OpenFilesCacheCapacity", dbMaxOpenFiles), - WriteBuffer: debugEnvValue("WriteBuffer", defaultWriteBuffer), - // The write slowdown and pause can be overridden, but even if they - // are not and the compaction trigger is overridden we need to - // adjust so that we don't pause writes for L0 compaction before we - // even *start* L0 compaction... - WriteL0SlowdownTrigger: debugEnvValue("WriteL0SlowdownTrigger", 2*debugEnvValue("CompactionL0Trigger", defaultCompactionL0Trigger)), - WriteL0PauseTrigger: debugEnvValue("WriteL0SlowdownTrigger", 3*debugEnvValue("CompactionL0Trigger", defaultCompactionL0Trigger)), - } - - return opts -} - -func open(location string, opts *opt.Options) (*leveldb.DB, error) { - db, err := leveldb.OpenFile(location, opts) - if leveldbIsCorrupted(err) { - db, err = leveldb.RecoverFile(location, opts) - } - if leveldbIsCorrupted(err) { - // The database is corrupted, and we've tried to recover it but it - // didn't work. At this point there isn't much to do beyond dropping - // the database and reindexing... - l.Infoln("Database corruption detected, unable to recover. Reinitializing...") - if err := os.RemoveAll(location); err != nil { - return nil, &errorSuggestion{err, "failed to delete corrupted database"} - } - db, err = leveldb.OpenFile(location, opts) - } - if err != nil { - return nil, &errorSuggestion{err, "is another instance of Syncthing running?"} - } - - if debugEnvValue("CompactEverything", 0) != 0 { - if err := db.CompactRange(util.Range{}); err != nil { - l.Warnln("Compacting database:", err) - } - } - - return db, nil -} - -func debugEnvValue(key string, def int) int { - v, err := strconv.ParseInt(os.Getenv("STDEBUG_"+key), 10, 63) - if err != nil { - return def - } - return int(v) -} - -// A "better" version of leveldb's errors.IsCorrupted. -func leveldbIsCorrupted(err error) bool { - switch { - case err == nil: - return false - - case errors.IsCorrupted(err): - return true - - case strings.Contains(err.Error(), "corrupted"): - return true - } - - return false -} - -// dbIsLarge returns whether the estimated size of the database at location -// is large enough to warrant optimization for large databases. -func dbIsLarge(location string) bool { - if ^uint(0)>>63 == 0 { - // We're compiled for a 32 bit architecture. We've seen trouble with - // large settings there. - // (https://forum.syncthing.net/t/many-small-ldb-files-with-database-tuning/13842) - return false - } - - entries, err := os.ReadDir(location) - if err != nil { - return false - } - - var size int64 - for _, entry := range entries { - if entry.Name() == "LOG" { - // don't count the size - continue - } - fi, err := entry.Info() - if err != nil { - continue - } - size += fi.Size() - } - - return size > dbLargeThreshold -} - -type errorSuggestion struct { - inner error - suggestion string -} - -func (e *errorSuggestion) Error() string { - return fmt.Sprintf("%s (%s)", e.inner.Error(), e.suggestion) -} diff --git a/lib/db/backend/leveldb_test.go b/lib/db/backend/leveldb_test.go deleted file mode 100644 index 3de46ad90..000000000 --- a/lib/db/backend/leveldb_test.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (C) 2019 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package backend - -import "testing" - -func TestLevelDBBackendBehavior(t *testing.T) { - testBackendBehavior(t, OpenLevelDBMemory) -} diff --git a/lib/db/benchmark_test.go b/lib/db/benchmark_test.go deleted file mode 100644 index 6497eb829..000000000 --- a/lib/db/benchmark_test.go +++ /dev/null @@ -1,344 +0,0 @@ -// Copyright (C) 2015 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package db_test - -import ( - "fmt" - "testing" - - "github.com/syncthing/syncthing/lib/db" - "github.com/syncthing/syncthing/lib/protocol" -) - -var files, oneFile, firstHalf, secondHalf, changed100, unchanged100 []protocol.FileInfo - -func lazyInitBenchFiles() { - if files != nil { - return - } - - files = make([]protocol.FileInfo, 0, 1000) - for i := 0; i < 1000; i++ { - files = append(files, protocol.FileInfo{ - Name: fmt.Sprintf("file%d", i), - Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, - Blocks: genBlocks(i), - }) - } - - middle := len(files) / 2 - firstHalf = files[:middle] - secondHalf = files[middle:] - oneFile = firstHalf[middle-1 : middle] - - unchanged100 := files[100:200] - changed100 := append([]protocol.FileInfo{}, unchanged100...) - for i := range changed100 { - changed100[i].Version = changed100[i].Version.Copy().Update(myID) - } -} - -func getBenchFileSet(b testing.TB) (*db.Lowlevel, *db.FileSet) { - lazyInitBenchFiles() - - ldb := newLowlevelMemory(b) - benchS := newFileSet(b, "test)", ldb) - replace(benchS, remoteDevice0, files) - replace(benchS, protocol.LocalDeviceID, firstHalf) - - return ldb, benchS -} - -func BenchmarkReplaceAll(b *testing.B) { - ldb := newLowlevelMemory(b) - defer ldb.Close() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - m := newFileSet(b, "test)", ldb) - replace(m, protocol.LocalDeviceID, files) - } - - b.ReportAllocs() -} - -func BenchmarkUpdateOneChanged(b *testing.B) { - ldb, benchS := getBenchFileSet(b) - defer ldb.Close() - - changed := make([]protocol.FileInfo, 1) - changed[0] = oneFile[0] - changed[0].Version = changed[0].Version.Copy().Update(myID) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - if i%2 == 0 { - benchS.Update(protocol.LocalDeviceID, changed) - } else { - benchS.Update(protocol.LocalDeviceID, oneFile) - } - } - - b.ReportAllocs() -} - -func BenchmarkUpdate100Changed(b *testing.B) { - ldb, benchS := getBenchFileSet(b) - defer ldb.Close() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - if i%2 == 0 { - benchS.Update(protocol.LocalDeviceID, changed100) - } else { - benchS.Update(protocol.LocalDeviceID, unchanged100) - } - } - - b.ReportAllocs() -} - -func setup10Remotes(benchS *db.FileSet) { - idBase := remoteDevice1.String()[1:] - first := 'J' - for i := 0; i < 10; i++ { - id, _ := protocol.DeviceIDFromString(fmt.Sprintf("%v%s", first+rune(i), idBase)) - if i%2 == 0 { - benchS.Update(id, changed100) - } else { - benchS.Update(id, unchanged100) - } - } -} - -func BenchmarkUpdate100Changed10Remotes(b *testing.B) { - ldb, benchS := getBenchFileSet(b) - defer ldb.Close() - - setup10Remotes(benchS) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - if i%2 == 0 { - benchS.Update(protocol.LocalDeviceID, changed100) - } else { - benchS.Update(protocol.LocalDeviceID, unchanged100) - } - } - - b.ReportAllocs() -} - -func BenchmarkUpdate100ChangedRemote(b *testing.B) { - ldb, benchS := getBenchFileSet(b) - defer ldb.Close() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - if i%2 == 0 { - benchS.Update(remoteDevice0, changed100) - } else { - benchS.Update(remoteDevice0, unchanged100) - } - } - - b.ReportAllocs() -} - -func BenchmarkUpdate100ChangedRemote10Remotes(b *testing.B) { - ldb, benchS := getBenchFileSet(b) - defer ldb.Close() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - if i%2 == 0 { - benchS.Update(remoteDevice0, changed100) - } else { - benchS.Update(remoteDevice0, unchanged100) - } - } - - b.ReportAllocs() -} - -func BenchmarkUpdateOneUnchanged(b *testing.B) { - ldb, benchS := getBenchFileSet(b) - defer ldb.Close() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchS.Update(protocol.LocalDeviceID, oneFile) - } - - b.ReportAllocs() -} - -func BenchmarkNeedHalf(b *testing.B) { - ldb, benchS := getBenchFileSet(b) - defer ldb.Close() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - count := 0 - snap := snapshot(b, benchS) - snap.WithNeed(protocol.LocalDeviceID, func(fi protocol.FileInfo) bool { - count++ - return true - }) - snap.Release() - if count != len(secondHalf) { - b.Errorf("wrong length %d != %d", count, len(secondHalf)) - } - } - - b.ReportAllocs() -} - -func BenchmarkNeedHalfRemote(b *testing.B) { - ldb := newLowlevelMemory(b) - defer ldb.Close() - fset := newFileSet(b, "test)", ldb) - replace(fset, remoteDevice0, firstHalf) - replace(fset, protocol.LocalDeviceID, files) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - count := 0 - snap := snapshot(b, fset) - snap.WithNeed(remoteDevice0, func(fi protocol.FileInfo) bool { - count++ - return true - }) - snap.Release() - if count != len(secondHalf) { - b.Errorf("wrong length %d != %d", count, len(secondHalf)) - } - } - - b.ReportAllocs() -} - -func BenchmarkHave(b *testing.B) { - ldb, benchS := getBenchFileSet(b) - defer ldb.Close() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - count := 0 - snap := snapshot(b, benchS) - snap.WithHave(protocol.LocalDeviceID, func(fi protocol.FileInfo) bool { - count++ - return true - }) - snap.Release() - if count != len(firstHalf) { - b.Errorf("wrong length %d != %d", count, len(firstHalf)) - } - } - - b.ReportAllocs() -} - -func BenchmarkGlobal(b *testing.B) { - ldb, benchS := getBenchFileSet(b) - defer ldb.Close() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - count := 0 - snap := snapshot(b, benchS) - snap.WithGlobal(func(fi protocol.FileInfo) bool { - count++ - return true - }) - snap.Release() - if count != len(files) { - b.Errorf("wrong length %d != %d", count, len(files)) - } - } - - b.ReportAllocs() -} - -func BenchmarkNeedHalfTruncated(b *testing.B) { - ldb, benchS := getBenchFileSet(b) - defer ldb.Close() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - count := 0 - snap := snapshot(b, benchS) - snap.WithNeedTruncated(protocol.LocalDeviceID, func(fi protocol.FileInfo) bool { - count++ - return true - }) - snap.Release() - if count != len(secondHalf) { - b.Errorf("wrong length %d != %d", count, len(secondHalf)) - } - } - - b.ReportAllocs() -} - -func BenchmarkHaveTruncated(b *testing.B) { - ldb, benchS := getBenchFileSet(b) - defer ldb.Close() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - count := 0 - snap := snapshot(b, benchS) - snap.WithHaveTruncated(protocol.LocalDeviceID, func(fi protocol.FileInfo) bool { - count++ - return true - }) - snap.Release() - if count != len(firstHalf) { - b.Errorf("wrong length %d != %d", count, len(firstHalf)) - } - } - - b.ReportAllocs() -} - -func BenchmarkGlobalTruncated(b *testing.B) { - ldb, benchS := getBenchFileSet(b) - defer ldb.Close() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - count := 0 - snap := snapshot(b, benchS) - snap.WithGlobalTruncated(func(fi protocol.FileInfo) bool { - count++ - return true - }) - snap.Release() - if count != len(files) { - b.Errorf("wrong length %d != %d", count, len(files)) - } - } - - b.ReportAllocs() -} - -func BenchmarkNeedCount(b *testing.B) { - ldb, benchS := getBenchFileSet(b) - defer ldb.Close() - - benchS.Update(protocol.LocalDeviceID, changed100) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - snap := snapshot(b, benchS) - _ = snap.NeedSize(protocol.LocalDeviceID) - snap.Release() - } - - b.ReportAllocs() -} diff --git a/lib/db/blockmap.go b/lib/db/blockmap.go deleted file mode 100644 index 78d71c0b1..000000000 --- a/lib/db/blockmap.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (C) 2014 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package db - -import ( - "encoding/binary" - "fmt" - - "github.com/syncthing/syncthing/lib/osutil" -) - -type BlockFinder struct { - db *Lowlevel -} - -func NewBlockFinder(db *Lowlevel) *BlockFinder { - return &BlockFinder{ - db: db, - } -} - -func (f *BlockFinder) String() string { - return fmt.Sprintf("BlockFinder@%p", f) -} - -// Iterate takes an iterator function which iterates over all matching blocks -// for the given hash. The iterator function has to return either true (if -// they are happy with the block) or false to continue iterating for whatever -// reason. The iterator finally returns the result, whether or not a -// satisfying block was eventually found. -func (f *BlockFinder) Iterate(folders []string, hash []byte, iterFn func(string, string, int32) bool) bool { - t, err := f.db.newReadOnlyTransaction() - if err != nil { - return false - } - defer t.close() - - var key []byte - for _, folder := range folders { - key, err = f.db.keyer.GenerateBlockMapKey(key, []byte(folder), hash, nil) - if err != nil { - return false - } - iter, err := t.NewPrefixIterator(key) - if err != nil { - return false - } - - for iter.Next() && iter.Error() == nil { - file := string(f.db.keyer.NameFromBlockMapKey(iter.Key())) - index := int32(binary.BigEndian.Uint32(iter.Value())) - if iterFn(folder, osutil.NativeFilename(file), index) { - iter.Release() - return true - } - } - iter.Release() - } - return false -} diff --git a/lib/db/blockmap_test.go b/lib/db/blockmap_test.go deleted file mode 100644 index e5d298836..000000000 --- a/lib/db/blockmap_test.go +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright (C) 2014 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package db - -import ( - "encoding/binary" - "testing" - - "github.com/syncthing/syncthing/lib/protocol" -) - -var ( - f1, f2, f3 protocol.FileInfo - folders = []string{"folder1", "folder2"} -) - -func init() { - blocks := genBlocks(30) - - f1 = protocol.FileInfo{ - Name: "f1", - Blocks: blocks[:10], - } - - f2 = protocol.FileInfo{ - Name: "f2", - Blocks: blocks[10:20], - } - - f3 = protocol.FileInfo{ - Name: "f3", - Blocks: blocks[20:], - } -} - -func setup(t testing.TB) (*Lowlevel, *BlockFinder) { - t.Helper() - db := newLowlevelMemory(t) - return db, NewBlockFinder(db) -} - -func dbEmpty(db *Lowlevel) bool { - iter, err := db.NewPrefixIterator([]byte{KeyTypeBlock}) - if err != nil { - panic(err) - } - defer iter.Release() - return !iter.Next() -} - -func addToBlockMap(db *Lowlevel, folder []byte, fs []protocol.FileInfo) error { - t, err := db.newReadWriteTransaction() - if err != nil { - return err - } - defer t.close() - - var keyBuf []byte - blockBuf := make([]byte, 4) - for _, f := range fs { - if !f.IsDirectory() && !f.IsDeleted() && !f.IsInvalid() { - name := []byte(f.Name) - for i, block := range f.Blocks { - binary.BigEndian.PutUint32(blockBuf, uint32(i)) - keyBuf, err = t.keyer.GenerateBlockMapKey(keyBuf, folder, block.Hash, name) - if err != nil { - return err - } - if err := t.Put(keyBuf, blockBuf); err != nil { - return err - } - } - } - } - return t.Commit() -} - -func discardFromBlockMap(db *Lowlevel, folder []byte, fs []protocol.FileInfo) error { - t, err := db.newReadWriteTransaction() - if err != nil { - return err - } - defer t.close() - - var keyBuf []byte - for _, ef := range fs { - if !ef.IsDirectory() && !ef.IsDeleted() && !ef.IsInvalid() { - name := []byte(ef.Name) - for _, block := range ef.Blocks { - keyBuf, err = t.keyer.GenerateBlockMapKey(keyBuf, folder, block.Hash, name) - if err != nil { - return err - } - if err := t.Delete(keyBuf); err != nil { - return err - } - } - } - } - return t.Commit() -} - -func TestBlockMapAddUpdateWipe(t *testing.T) { - db, f := setup(t) - defer db.Close() - - if !dbEmpty(db) { - t.Fatal("db not empty") - } - - folder := []byte("folder1") - - f3.Type = protocol.FileInfoTypeDirectory - - if err := addToBlockMap(db, folder, []protocol.FileInfo{f1, f2, f3}); err != nil { - t.Fatal(err) - } - - f.Iterate(folders, f1.Blocks[0].Hash, func(folder, file string, index int32) bool { - if folder != "folder1" || file != "f1" || index != 0 { - t.Fatal("Mismatch") - } - return true - }) - - f.Iterate(folders, f2.Blocks[0].Hash, func(folder, file string, index int32) bool { - if folder != "folder1" || file != "f2" || index != 0 { - t.Fatal("Mismatch") - } - return true - }) - - f.Iterate(folders, f3.Blocks[0].Hash, func(folder, file string, index int32) bool { - t.Fatal("Unexpected block") - return true - }) - - if err := discardFromBlockMap(db, folder, []protocol.FileInfo{f1, f2, f3}); err != nil { - t.Fatal(err) - } - - f1.Deleted = true - f2.LocalFlags = protocol.FlagLocalMustRescan // one of the invalid markers - - if err := addToBlockMap(db, folder, []protocol.FileInfo{f1, f2, f3}); err != nil { - t.Fatal(err) - } - - f.Iterate(folders, f1.Blocks[0].Hash, func(folder, file string, index int32) bool { - t.Fatal("Unexpected block") - return false - }) - - f.Iterate(folders, f2.Blocks[0].Hash, func(folder, file string, index int32) bool { - t.Fatal("Unexpected block") - return false - }) - - f.Iterate(folders, f3.Blocks[0].Hash, func(folder, file string, index int32) bool { - if folder != "folder1" || file != "f3" || index != 0 { - t.Fatal("Mismatch") - } - return true - }) - - if err := db.dropFolder(folder); err != nil { - t.Fatal(err) - } - - if !dbEmpty(db) { - t.Fatal("db not empty") - } - - // Should not add - if err := addToBlockMap(db, folder, []protocol.FileInfo{f1, f2}); err != nil { - t.Fatal(err) - } - - if !dbEmpty(db) { - t.Fatal("db not empty") - } - - f1.Deleted = false - f1.LocalFlags = 0 - f2.Deleted = false - f2.LocalFlags = 0 - f3.Deleted = false - f3.LocalFlags = 0 -} - -func TestBlockFinderLookup(t *testing.T) { - db, f := setup(t) - defer db.Close() - - folder1 := []byte("folder1") - folder2 := []byte("folder2") - - if err := addToBlockMap(db, folder1, []protocol.FileInfo{f1}); err != nil { - t.Fatal(err) - } - if err := addToBlockMap(db, folder2, []protocol.FileInfo{f1}); err != nil { - t.Fatal(err) - } - - counter := 0 - f.Iterate(folders, f1.Blocks[0].Hash, func(folder, file string, index int32) bool { - counter++ - switch counter { - case 1: - if folder != "folder1" || file != "f1" || index != 0 { - t.Fatal("Mismatch") - } - case 2: - if folder != "folder2" || file != "f1" || index != 0 { - t.Fatal("Mismatch") - } - default: - t.Fatal("Unexpected block") - } - return false - }) - - if counter != 2 { - t.Fatal("Incorrect count", counter) - } - - if err := discardFromBlockMap(db, folder1, []protocol.FileInfo{f1}); err != nil { - t.Fatal(err) - } - - f1.Deleted = true - - if err := addToBlockMap(db, folder1, []protocol.FileInfo{f1}); err != nil { - t.Fatal(err) - } - - counter = 0 - f.Iterate(folders, f1.Blocks[0].Hash, func(folder, file string, index int32) bool { - counter++ - switch counter { - case 1: - if folder != "folder2" || file != "f1" || index != 0 { - t.Fatal("Mismatch") - } - default: - t.Fatal("Unexpected block") - } - return false - }) - - if counter != 1 { - t.Fatal("Incorrect count") - } - - f1.Deleted = false -} diff --git a/lib/db/db_test.go b/lib/db/db_test.go deleted file mode 100644 index 910e6767c..000000000 --- a/lib/db/db_test.go +++ /dev/null @@ -1,701 +0,0 @@ -// Copyright (C) 2014 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package db - -import ( - "bytes" - "context" - "fmt" - "testing" - - "github.com/syncthing/syncthing/lib/db/backend" - "github.com/syncthing/syncthing/lib/events" - "github.com/syncthing/syncthing/lib/protocol" -) - -func genBlocks(n int) []protocol.BlockInfo { - b := make([]protocol.BlockInfo, n) - for i := range b { - h := make([]byte, 32) - for j := range h { - h[j] = byte(i + j) - } - b[i].Size = i - b[i].Hash = h - } - return b -} - -const myID = 1 - -var ( - remoteDevice0, remoteDevice1 protocol.DeviceID - invalid = "invalid" - slashPrefixed = "/notgood" - haveUpdate0to3 map[protocol.DeviceID][]protocol.FileInfo -) - -func init() { - remoteDevice0, _ = protocol.DeviceIDFromString("AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR") - remoteDevice1, _ = protocol.DeviceIDFromString("I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU") - haveUpdate0to3 = map[protocol.DeviceID][]protocol.FileInfo{ - protocol.LocalDeviceID: { - protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)}, - protocol.FileInfo{Name: slashPrefixed, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)}, - }, - remoteDevice0: { - protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)}, - protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), RawInvalid: true}, - protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)}, - }, - remoteDevice1: { - protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(7)}, - protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(5), RawInvalid: true}, - protocol.FileInfo{Name: invalid, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), RawInvalid: true}, - }, - } -} - -// TestRepairSequence checks that a few hand-crafted messed-up sequence entries get fixed. -func TestRepairSequence(t *testing.T) { - db := newLowlevelMemory(t) - defer db.Close() - - folderStr := "test" - folder := []byte(folderStr) - id := protocol.LocalDeviceID - short := protocol.LocalDeviceID.Short() - - files := []protocol.FileInfo{ - {Name: "fine", Blocks: genBlocks(1)}, - {Name: "duplicate", Blocks: genBlocks(2)}, - {Name: "missing", Blocks: genBlocks(3)}, - {Name: "overwriting", Blocks: genBlocks(4)}, - {Name: "inconsistent", Blocks: genBlocks(5)}, - {Name: "inconsistentNotIndirected", Blocks: genBlocks(2)}, - } - for i, f := range files { - files[i].Version = f.Version.Update(short) - } - - trans, err := db.newReadWriteTransaction() - if err != nil { - t.Fatal(err) - } - defer trans.close() - - addFile := func(f protocol.FileInfo, seq int64) { - dk, err := trans.keyer.GenerateDeviceFileKey(nil, folder, id[:], []byte(f.Name)) - if err != nil { - t.Fatal(err) - } - if err := trans.putFile(dk, f); err != nil { - t.Fatal(err) - } - sk, err := trans.keyer.GenerateSequenceKey(nil, folder, seq) - if err != nil { - t.Fatal(err) - } - if err := trans.Put(sk, dk); err != nil { - t.Fatal(err) - } - } - - // Plain normal entry - var seq int64 = 1 - files[0].Sequence = 1 - addFile(files[0], seq) - - // Second entry once updated with original sequence still in place - f := files[1] - f.Sequence = int64(len(files) + 1) - addFile(f, f.Sequence) - // Original sequence entry - seq++ - sk, err := trans.keyer.GenerateSequenceKey(nil, folder, seq) - if err != nil { - t.Fatal(err) - } - dk, err := trans.keyer.GenerateDeviceFileKey(nil, folder, id[:], []byte(f.Name)) - if err != nil { - t.Fatal(err) - } - if err := trans.Put(sk, dk); err != nil { - t.Fatal(err) - } - - // File later overwritten thus missing sequence entry - seq++ - files[2].Sequence = seq - addFile(files[2], seq) - - // File overwriting previous sequence entry (no seq bump) - seq++ - files[3].Sequence = seq - addFile(files[3], seq) - - // Inconistent files - seq++ - files[4].Sequence = 101 - addFile(files[4], seq) - seq++ - files[5].Sequence = 102 - addFile(files[5], seq) - - // And a sequence entry pointing at nothing because why not - sk, err = trans.keyer.GenerateSequenceKey(nil, folder, 100001) - if err != nil { - t.Fatal(err) - } - dk, err = trans.keyer.GenerateDeviceFileKey(nil, folder, id[:], []byte("nonexisting")) - if err != nil { - t.Fatal(err) - } - if err := trans.Put(sk, dk); err != nil { - t.Fatal(err) - } - - if err := trans.Commit(); err != nil { - t.Fatal(err) - } - - // Loading the metadata for the first time means a "re"calculation happens, - // along which the sequences get repaired too. - db.gcMut.RLock() - _, err = db.loadMetadataTracker(folderStr) - db.gcMut.RUnlock() - if err != nil { - t.Fatal(err) - } - - // Check the db - ro, err := db.newReadOnlyTransaction() - if err != nil { - t.Fatal(err) - } - defer ro.close() - - it, err := ro.NewPrefixIterator([]byte{KeyTypeDevice}) - if err != nil { - t.Fatal(err) - } - defer it.Release() - for it.Next() { - fi, err := ro.unmarshalTrunc(it.Value(), true) - if err != nil { - t.Fatal(err) - } - if sk, err = ro.keyer.GenerateSequenceKey(sk, folder, fi.SequenceNo()); err != nil { - t.Fatal(err) - } - dk, err := ro.Get(sk) - if backend.IsNotFound(err) { - t.Error("Missing sequence entry for", fi.FileName()) - } else if err != nil { - t.Fatal(err) - } - if !bytes.Equal(it.Key(), dk) { - t.Errorf("Wrong key for %v, expected %s, got %s", f.FileName(), it.Key(), dk) - } - } - if err := it.Error(); err != nil { - t.Fatal(err) - } - it.Release() - - it, err = ro.NewPrefixIterator([]byte{KeyTypeSequence}) - if err != nil { - t.Fatal(err) - } - defer it.Release() - for it.Next() { - fi, ok, err := ro.getFileTrunc(it.Value(), false) - if err != nil { - t.Fatal(err) - } - seq := ro.keyer.SequenceFromSequenceKey(it.Key()) - if !ok { - t.Errorf("Sequence entry %v points at nothing", seq) - } else if fi.SequenceNo() != seq { - t.Errorf("Inconsistent sequence entry for %v: %v != %v", fi.FileName(), fi.SequenceNo(), seq) - } - if len(fi.Blocks) == 0 { - t.Error("Missing blocks in", fi.FileName()) - } - } - if err := it.Error(); err != nil { - t.Fatal(err) - } - it.Release() -} - -func TestDowngrade(t *testing.T) { - db := newLowlevelMemory(t) - defer db.Close() - // sets the min version etc - if err := UpdateSchema(db); err != nil { - t.Fatal(err) - } - - // Bump the database version to something newer than we actually support - miscDB := NewMiscDataNamespace(db) - if err := miscDB.PutInt64("dbVersion", dbVersion+1); err != nil { - t.Fatal(err) - } - l.Infoln(dbVersion) - - // Pretend we just opened the DB and attempt to update it again - err := UpdateSchema(db) - - if err, ok := err.(*databaseDowngradeError); !ok { - t.Fatal("Expected error due to database downgrade, got", err) - } else if err.minSyncthingVersion != dbMinSyncthingVersion { - t.Fatalf("Error has %v as min Syncthing version, expected %v", err.minSyncthingVersion, dbMinSyncthingVersion) - } -} - -func TestCheckGlobals(t *testing.T) { - db := newLowlevelMemory(t) - defer db.Close() - - fs := newFileSet(t, "test", db) - - // Add any file - name := "foo" - fs.Update(protocol.LocalDeviceID, []protocol.FileInfo{ - { - Name: name, - Type: protocol.FileInfoTypeFile, - Version: protocol.Vector{Counters: []protocol.Counter{{ID: 1, Value: 1001}}}, - }, - }) - - // Remove just the file entry - if err := db.dropPrefix([]byte{KeyTypeDevice}); err != nil { - t.Fatal(err) - } - - // Clean up global entry of the now missing file - if repaired, err := db.checkGlobals(fs.folder); err != nil { - t.Fatal(err) - } else if repaired != 1 { - t.Error("Expected 1 repaired global item, got", repaired) - } - - // Check that the global entry is gone - gk, err := db.keyer.GenerateGlobalVersionKey(nil, []byte(fs.folder), []byte(name)) - if err != nil { - t.Fatal(err) - } - _, err = db.Get(gk) - if !backend.IsNotFound(err) { - t.Error("Expected key missing error, got", err) - } -} - -func TestDropDuplicates(t *testing.T) { - names := []string{ - "foo", - "bar", - "dcxvoijnds", - "3d/dsfase/4/ss2", - } - tcs := []struct{ in, out []int }{ - {[]int{0}, []int{0}}, - {[]int{0, 1}, []int{0, 1}}, - {[]int{0, 1, 0, 1}, []int{0, 1}}, - {[]int{0, 1, 1, 1, 1}, []int{0, 1}}, - {[]int{0, 0, 0, 1}, []int{0, 1}}, - {[]int{0, 1, 2, 3}, []int{0, 1, 2, 3}}, - {[]int{3, 2, 1, 0, 0, 1, 2, 3}, []int{0, 1, 2, 3}}, - {[]int{0, 1, 1, 3, 0, 1, 0, 1, 2, 3}, []int{0, 1, 2, 3}}, - } - - for tci, tc := range tcs { - inp := make([]protocol.FileInfo, len(tc.in)) - expSeq := make(map[string]int) - for i, j := range tc.in { - inp[i] = protocol.FileInfo{Name: names[j], Sequence: int64(i)} - expSeq[names[j]] = i - } - outp := normalizeFilenamesAndDropDuplicates(inp) - if len(outp) != len(tc.out) { - t.Errorf("tc %v: Expected %v entries, got %v", tci, len(tc.out), len(outp)) - continue - } - for i, f := range outp { - if exp := names[tc.out[i]]; exp != f.Name { - t.Errorf("tc %v: Got file %v at pos %v, expected %v", tci, f.Name, i, exp) - } - if exp := int64(expSeq[outp[i].Name]); exp != f.Sequence { - t.Errorf("tc %v: Got sequence %v at pos %v, expected %v", tci, f.Sequence, i, exp) - } - } - } -} - -func TestGCIndirect(t *testing.T) { - // Verify that the gcIndirect run actually removes block lists. - - db := newLowlevelMemory(t) - defer db.Close() - meta := newMetadataTracker(db.keyer, events.NoopLogger) - - // Add three files with different block lists - - files := []protocol.FileInfo{ - {Name: "a", Blocks: genBlocks(100)}, - {Name: "b", Blocks: genBlocks(200)}, - {Name: "c", Blocks: genBlocks(300)}, - } - - db.updateLocalFiles([]byte("folder"), files, meta) - - // Run a GC pass - - db.gcIndirect(context.Background()) - - // Verify that we have three different block lists - - n, err := numBlockLists(db) - if err != nil { - t.Fatal(err) - } - if n != len(files) { - t.Fatal("expected each file to have a block list") - } - - // Change the block lists for each file - - for i := range files { - files[i].Version = files[i].Version.Update(42) - files[i].Blocks = genBlocks(len(files[i].Blocks) + 1) - } - - db.updateLocalFiles([]byte("folder"), files, meta) - - // Verify that we now have *six* different block lists - - n, err = numBlockLists(db) - if err != nil { - t.Fatal(err) - } - if n != 2*len(files) { - t.Fatal("expected both old and new block lists to exist") - } - - // Run a GC pass - - db.gcIndirect(context.Background()) - - // Verify that we now have just the three we need, again - - n, err = numBlockLists(db) - if err != nil { - t.Fatal(err) - } - if n != len(files) { - t.Fatal("expected GC to collect all but the needed ones") - } - - // Double check the correctness by loading the block lists and comparing with what we stored - - tr, err := db.newReadOnlyTransaction() - if err != nil { - t.Fatal() - } - defer tr.Release() - for _, f := range files { - fi, ok, err := tr.getFile([]byte("folder"), protocol.LocalDeviceID[:], []byte(f.Name)) - if err != nil { - t.Fatal(err) - } - if !ok { - t.Fatal("mysteriously missing") - } - if len(fi.Blocks) != len(f.Blocks) { - t.Fatal("block list mismatch") - } - for i := range fi.Blocks { - if !bytes.Equal(fi.Blocks[i].Hash, f.Blocks[i].Hash) { - t.Fatal("hash mismatch") - } - } - } -} - -func TestUpdateTo14(t *testing.T) { - db := newLowlevelMemory(t) - defer db.Close() - - folderStr := "default" - folder := []byte(folderStr) - name := []byte("foo") - file := protocol.FileInfo{Name: string(name), Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(blocksIndirectionCutoff - 1)} - file.BlocksHash = protocol.BlocksHash(file.Blocks) - fileWOBlocks := file - fileWOBlocks.Blocks = nil - meta, err := db.loadMetadataTracker(folderStr) - if err != nil { - t.Fatal(err) - } - - // Initially add the correct file the usual way, all good here. - if err := db.updateLocalFiles(folder, []protocol.FileInfo{file}, meta); err != nil { - t.Fatal(err) - } - - // Simulate the previous bug, where .putFile could write a file info without - // blocks, even though the file has them (and thus a non-nil BlocksHash). - trans, err := db.newReadWriteTransaction() - if err != nil { - t.Fatal(err) - } - defer trans.close() - key, err := db.keyer.GenerateDeviceFileKey(nil, folder, protocol.LocalDeviceID[:], name) - if err != nil { - t.Fatal(err) - } - fiBs := mustMarshal(fileWOBlocks.ToWire(true)) - if err := trans.Put(key, fiBs); err != nil { - t.Fatal(err) - } - if err := trans.Commit(); err != nil { - t.Fatal(err) - } - trans.close() - - // Run migration, pretending were still on schema 13. - if err := (&schemaUpdater{db}).updateSchemaTo14(13); err != nil { - t.Fatal(err) - } - - // checks - ro, err := db.newReadOnlyTransaction() - if err != nil { - t.Fatal(err) - } - defer ro.close() - if f, ok, err := ro.getFileByKey(key); err != nil { - t.Fatal(err) - } else if !ok { - t.Error("file missing") - } else if !f.MustRescan() { - t.Error("file not marked as MustRescan") - } - - if vl, err := ro.getGlobalVersions(nil, folder, name); err != nil { - t.Fatal(err) - } else if fv, ok := vlGetGlobal(vl); !ok { - t.Error("missing global") - } else if !fvIsInvalid(fv) { - t.Error("global not marked as invalid") - } -} - -func TestFlushRecursion(t *testing.T) { - // Verify that a commit hook can write to the transaction without - // causing another flush and thus recursion. - - db := newLowlevelMemory(t) - defer db.Close() - - // A commit hook that writes a small piece of data to the transaction. - hookFired := 0 - hook := func(tx backend.WriteTransaction) error { - err := tx.Put([]byte(fmt.Sprintf("hook-key-%d", hookFired)), []byte(fmt.Sprintf("hook-value-%d", hookFired))) - if err != nil { - t.Fatal(err) - } - hookFired++ - return nil - } - - // A transaction. - tx, err := db.NewWriteTransaction(hook) - if err != nil { - t.Fatal(err) - } - defer tx.Release() - - // Write stuff until the transaction flushes, thus firing the hook. - i := 0 - for hookFired == 0 { - err := tx.Put([]byte(fmt.Sprintf("key-%d", i)), []byte(fmt.Sprintf("value-%d", i))) - if err != nil { - t.Fatal(err) - } - i++ - } - - // The hook should have fired precisely once. - if hookFired != 1 { - t.Error("expect one hook fire, not", hookFired) - } -} - -func TestCheckLocalNeed(t *testing.T) { - db := newLowlevelMemory(t) - defer db.Close() - - folderStr := "test" - fs := newFileSet(t, folderStr, db) - - // Add files such that we are in sync for a and b, and need c and d. - files := []protocol.FileInfo{ - {Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1}}}}, - {Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1}}}}, - {Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1}}}}, - {Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1}}}}, - } - fs.Update(protocol.LocalDeviceID, files) - files[2].Version = files[2].Version.Update(remoteDevice0.Short()) - files[3].Version = files[2].Version.Update(remoteDevice0.Short()) - fs.Update(remoteDevice0, files) - - checkNeed := func() { - snap := snapshot(t, fs) - defer snap.Release() - c := snap.NeedSize(protocol.LocalDeviceID) - if c.Files != 2 { - t.Errorf("Expected 2 needed files locally, got %v in meta", c.Files) - } - needed := make([]protocol.FileInfo, 0, 2) - snap.WithNeed(protocol.LocalDeviceID, func(fi protocol.FileInfo) bool { - needed = append(needed, fi) - return true - }) - if l := len(needed); l != 2 { - t.Errorf("Expected 2 needed files locally, got %v in db", l) - } else if needed[0].Name != "c" || needed[1].Name != "d" { - t.Errorf("Expected files c and d to be needed, got %v and %v", needed[0].Name, needed[1].Name) - } - } - - checkNeed() - - trans, err := db.newReadWriteTransaction() - if err != nil { - t.Fatal(err) - } - defer trans.close() - - // Add "b" to needed and remove "d" - folder := []byte(folderStr) - key, err := trans.keyer.GenerateNeedFileKey(nil, folder, []byte(files[1].Name)) - if err != nil { - t.Fatal(err) - } - if err = trans.Put(key, nil); err != nil { - t.Fatal(err) - } - key, err = trans.keyer.GenerateNeedFileKey(nil, folder, []byte(files[3].Name)) - if err != nil { - t.Fatal(err) - } - if err = trans.Delete(key); err != nil { - t.Fatal(err) - } - if err := trans.Commit(); err != nil { - t.Fatal(err) - } - - if repaired, err := db.checkLocalNeed(folder); err != nil { - t.Fatal(err) - } else if repaired != 2 { - t.Error("Expected 2 repaired local need items, got", repaired) - } - - checkNeed() -} - -func TestDuplicateNeedCount(t *testing.T) { - db := newLowlevelMemory(t) - defer db.Close() - - folder := "test" - - fs := newFileSet(t, folder, db) - files := []protocol.FileInfo{{Name: "foo", Version: protocol.Vector{}.Update(myID), Sequence: 1}} - fs.Update(protocol.LocalDeviceID, files) - files[0].Version = files[0].Version.Update(remoteDevice0.Short()) - fs.Update(remoteDevice0, files) - - db.checkRepair() - - fs = newFileSet(t, folder, db) - found := false - for _, c := range fs.meta.counts.Counts { - if protocol.LocalDeviceID == c.DeviceID && c.LocalFlags == needFlag { - if found { - t.Fatal("second need count for local device encountered") - } - found = true - } - } - if !found { - t.Fatal("no need count for local device encountered") - } -} - -func TestNeedAfterDropGlobal(t *testing.T) { - db := newLowlevelMemory(t) - defer db.Close() - - folder := "test" - - fs := newFileSet(t, folder, db) - - // Initial: - // Three devices and a file "test": local has Version 1, remoteDevice0 - // Version 2 and remoteDevice2 doesn't have it. - // All of them have "bar", just so the db knows about remoteDevice2. - files := []protocol.FileInfo{ - {Name: "foo", Version: protocol.Vector{}.Update(myID), Sequence: 1}, - {Name: "bar", Version: protocol.Vector{}.Update(myID), Sequence: 2}, - } - fs.Update(protocol.LocalDeviceID, files) - files[0].Version = files[0].Version.Update(myID) - fs.Update(remoteDevice0, files) - fs.Update(remoteDevice1, files[1:]) - - // remoteDevice1 needs one file: test - snap := snapshot(t, fs) - c := snap.NeedSize(remoteDevice1) - if c.Files != 1 { - t.Errorf("Expected 1 needed files initially, got %v", c.Files) - } - snap.Release() - - // Drop remoteDevice0, i.e. remove all their files from db. - // That changes the global file, which is now what local has. - fs.Drop(remoteDevice0) - - // remoteDevice1 still needs test. - snap = snapshot(t, fs) - c = snap.NeedSize(remoteDevice1) - if c.Files != 1 { - t.Errorf("Expected still 1 needed files, got %v", c.Files) - } - snap.Release() -} - -func numBlockLists(db *Lowlevel) (int, error) { - it, err := db.Backend.NewPrefixIterator([]byte{KeyTypeBlockList}) - if err != nil { - return 0, err - } - defer it.Release() - n := 0 - for it.Next() { - n++ - } - if err := it.Error(); err != nil { - return 0, err - } - return n, nil -} diff --git a/lib/db/keyer_test.go b/lib/db/keyer_test.go deleted file mode 100644 index 6b4372c9c..000000000 --- a/lib/db/keyer_test.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (C) 2018 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package db - -import ( - "bytes" - "testing" -) - -func TestDeviceKey(t *testing.T) { - fld := []byte("folder6789012345678901234567890123456789012345678901234567890123") - dev := []byte("device67890123456789012345678901") - name := []byte("name") - - db := newLowlevelMemory(t) - defer db.Close() - - key, err := db.keyer.GenerateDeviceFileKey(nil, fld, dev, name) - if err != nil { - t.Fatal(err) - } - - fld2, ok := db.keyer.FolderFromDeviceFileKey(key) - if !ok { - t.Fatal("unexpectedly not found") - } - if !bytes.Equal(fld2, fld) { - t.Errorf("wrong folder %q != %q", fld2, fld) - } - dev2, ok := db.keyer.DeviceFromDeviceFileKey(key) - if !ok { - t.Fatal("unexpectedly not found") - } - if !bytes.Equal(dev2, dev) { - t.Errorf("wrong device %q != %q", dev2, dev) - } - name2 := db.keyer.NameFromDeviceFileKey(key) - if !bytes.Equal(name2, name) { - t.Errorf("wrong name %q != %q", name2, name) - } -} - -func TestGlobalKey(t *testing.T) { - fld := []byte("folder6789012345678901234567890123456789012345678901234567890123") - name := []byte("name") - - db := newLowlevelMemory(t) - defer db.Close() - - key, err := db.keyer.GenerateGlobalVersionKey(nil, fld, name) - if err != nil { - t.Fatal(err) - } - - name2 := db.keyer.NameFromGlobalVersionKey(key) - if !bytes.Equal(name2, name) { - t.Errorf("wrong name %q != %q", name2, name) - } -} - -func TestSequenceKey(t *testing.T) { - fld := []byte("folder6789012345678901234567890123456789012345678901234567890123") - - db := newLowlevelMemory(t) - defer db.Close() - - const seq = 1234567890 - key, err := db.keyer.GenerateSequenceKey(nil, fld, seq) - if err != nil { - t.Fatal(err) - } - outSeq := db.keyer.SequenceFromSequenceKey(key) - if outSeq != seq { - t.Errorf("sequence number mangled, %d != %d", outSeq, seq) - } -} diff --git a/lib/db/lowlevel.go b/lib/db/lowlevel.go deleted file mode 100644 index 368f7ff99..000000000 --- a/lib/db/lowlevel.go +++ /dev/null @@ -1,1453 +0,0 @@ -// Copyright (C) 2014 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package db - -import ( - "bytes" - "context" - "crypto/sha256" - "encoding/binary" - "errors" - "fmt" - "hash/maphash" - "os" - "regexp" - "time" - - "github.com/greatroar/blobloom" - "github.com/thejerf/suture/v4" - "google.golang.org/protobuf/proto" - - "github.com/syncthing/syncthing/internal/gen/dbproto" - "github.com/syncthing/syncthing/lib/db/backend" - "github.com/syncthing/syncthing/lib/events" - "github.com/syncthing/syncthing/lib/fs" - "github.com/syncthing/syncthing/lib/protocol" - "github.com/syncthing/syncthing/lib/stringutil" - "github.com/syncthing/syncthing/lib/svcutil" - "github.com/syncthing/syncthing/lib/sync" -) - -const ( - // We set the bloom filter capacity to handle 100k individual items with - // a false positive probability of 1% for the first pass. Once we know - // how many items we have we will use that number instead, if it's more - // than 100k. For fewer than 100k items we will just get better false - // positive rate instead. - indirectGCBloomCapacity = 100000 - indirectGCBloomFalsePositiveRate = 0.01 // 1% - indirectGCBloomMaxBytes = 32 << 20 // Use at most 32MiB memory, which covers our desired FP rate at 27 M items - indirectGCDefaultInterval = 13 * time.Hour - indirectGCTimeKey = "lastIndirectGCTime" - - // Use indirection for the block list when it exceeds this many entries - blocksIndirectionCutoff = 3 - // Use indirection for the version vector when it exceeds this many entries - versionIndirectionCutoff = 10 - - recheckDefaultInterval = 30 * 24 * time.Hour - - needsRepairSuffix = ".needsrepair" -) - -// Lowlevel is the lowest level database interface. It has a very simple -// purpose: hold the actual backend database, and the in-memory state -// that belong to that database. In the same way that a single on disk -// database can only be opened once, there should be only one Lowlevel for -// any given backend. -type Lowlevel struct { - *suture.Supervisor - backend.Backend - folderIdx *smallIndex - deviceIdx *smallIndex - keyer keyer - gcMut sync.RWMutex - gcKeyCount int - indirectGCInterval time.Duration - recheckInterval time.Duration - oneFileSetCreated chan struct{} - evLogger events.Logger - - blockFilter *bloomFilter - versionFilter *bloomFilter -} - -func NewLowlevel(backend backend.Backend, evLogger events.Logger, opts ...Option) (*Lowlevel, error) { - // Only log restarts in debug mode. - spec := svcutil.SpecWithDebugLogger(l) - db := &Lowlevel{ - Supervisor: suture.New("db.Lowlevel", spec), - Backend: backend, - folderIdx: newSmallIndex(backend, []byte{KeyTypeFolderIdx}), - deviceIdx: newSmallIndex(backend, []byte{KeyTypeDeviceIdx}), - gcMut: sync.NewRWMutex(), - indirectGCInterval: indirectGCDefaultInterval, - recheckInterval: recheckDefaultInterval, - oneFileSetCreated: make(chan struct{}), - evLogger: evLogger, - } - for _, opt := range opts { - opt(db) - } - db.keyer = newDefaultKeyer(db.folderIdx, db.deviceIdx) - db.Add(svcutil.AsService(db.gcRunner, "db.Lowlevel/gcRunner")) - if path := db.needsRepairPath(); path != "" { - if _, err := os.Lstat(path); err == nil { - l.Infoln("Database was marked for repair - this may take a while") - if err := db.checkRepair(); err != nil { - db.handleFailure(err) - return nil, err - } - os.Remove(path) - } - } - return db, nil -} - -type Option func(*Lowlevel) - -// WithRecheckInterval sets the time interval in between metadata recalculations -// and consistency checks. -func WithRecheckInterval(dur time.Duration) Option { - return func(db *Lowlevel) { - if dur > 0 { - db.recheckInterval = dur - } - } -} - -// WithIndirectGCInterval sets the time interval in between GC runs. -func WithIndirectGCInterval(dur time.Duration) Option { - return func(db *Lowlevel) { - if dur > 0 { - db.indirectGCInterval = dur - } - } -} - -// ListFolders returns the list of folders currently in the database -func (db *Lowlevel) ListFolders() []string { - return db.folderIdx.Values() -} - -// updateRemoteFiles adds a list of fileinfos to the database and updates the -// global versionlist and metadata. -func (db *Lowlevel) updateRemoteFiles(folder, device []byte, fs []protocol.FileInfo, meta *metadataTracker) error { - db.gcMut.RLock() - defer db.gcMut.RUnlock() - - t, err := db.newReadWriteTransaction(meta.CommitHook(folder)) - if err != nil { - return err - } - defer t.close() - - var dk, gk, keyBuf []byte - devID, err := protocol.DeviceIDFromBytes(device) - if err != nil { - return err - } - for _, f := range fs { - name := []byte(f.Name) - dk, err = db.keyer.GenerateDeviceFileKey(dk, folder, device, name) - if err != nil { - return err - } - - ef, ok, err := t.getFileTrunc(dk, true) - if err != nil { - return err - } - - if ok { - meta.removeFile(devID, ef) - } - meta.addFile(devID, f) - - l.Debugf("insert (remote); folder=%q device=%v %v", folder, devID, f) - if err := t.putFile(dk, f); err != nil { - return err - } - - gk, err = db.keyer.GenerateGlobalVersionKey(gk, folder, name) - if err != nil { - return err - } - keyBuf, err = t.updateGlobal(gk, keyBuf, folder, device, f, meta) - if err != nil { - return err - } - - if err := t.Checkpoint(); err != nil { - return err - } - } - - return t.Commit() -} - -// updateLocalFiles adds fileinfos to the db, and updates the global versionlist, -// metadata, sequence and blockmap buckets. -func (db *Lowlevel) updateLocalFiles(folder []byte, fs []protocol.FileInfo, meta *metadataTracker) error { - db.gcMut.RLock() - defer db.gcMut.RUnlock() - - t, err := db.newReadWriteTransaction(meta.CommitHook(folder)) - if err != nil { - return err - } - defer t.close() - - var dk, gk, keyBuf []byte - blockBuf := make([]byte, 4) - for _, f := range fs { - name := []byte(f.Name) - dk, err = db.keyer.GenerateDeviceFileKey(dk, folder, protocol.LocalDeviceID[:], name) - if err != nil { - return err - } - - ef, ok, err := t.getFileByKey(dk) - if err != nil { - return err - } - - blocksHashSame := ok && bytes.Equal(ef.BlocksHash, f.BlocksHash) - if ok { - keyBuf, err = db.removeLocalBlockAndSequenceInfo(keyBuf, folder, name, ef, !blocksHashSame, &t) - if err != nil { - return err - } - } - - f.Sequence = meta.nextLocalSeq() - - if ok { - meta.removeFile(protocol.LocalDeviceID, ef) - } - meta.addFile(protocol.LocalDeviceID, f) - - l.Debugf("insert (local); folder=%q %v", folder, f) - if err := t.putFile(dk, f); err != nil { - return err - } - - gk, err = db.keyer.GenerateGlobalVersionKey(gk, folder, []byte(f.Name)) - if err != nil { - return err - } - keyBuf, err = t.updateGlobal(gk, keyBuf, folder, protocol.LocalDeviceID[:], f, meta) - if err != nil { - return err - } - - keyBuf, err = db.keyer.GenerateSequenceKey(keyBuf, folder, f.Sequence) - if err != nil { - return err - } - if err := t.Put(keyBuf, dk); err != nil { - return err - } - l.Debugf("adding sequence; folder=%q sequence=%v %v", folder, f.Sequence, f.Name) - - if len(f.Blocks) != 0 && !f.IsInvalid() && f.Size > 0 { - for i, block := range f.Blocks { - binary.BigEndian.PutUint32(blockBuf, uint32(i)) - keyBuf, err = db.keyer.GenerateBlockMapKey(keyBuf, folder, block.Hash, name) - if err != nil { - return err - } - if err := t.Put(keyBuf, blockBuf); err != nil { - return err - } - } - if !blocksHashSame { - keyBuf, err := db.keyer.GenerateBlockListMapKey(keyBuf, folder, f.BlocksHash, name) - if err != nil { - return err - } - if err = t.Put(keyBuf, nil); err != nil { - return err - } - } - } - - if err := t.Checkpoint(); err != nil { - return err - } - } - - return t.Commit() -} - -func (db *Lowlevel) removeLocalFiles(folder []byte, nameStrs []string, meta *metadataTracker) error { - db.gcMut.RLock() - defer db.gcMut.RUnlock() - - t, err := db.newReadWriteTransaction(meta.CommitHook(folder)) - if err != nil { - return err - } - defer t.close() - - var dk, gk, buf []byte - for _, nameStr := range nameStrs { - name := []byte(nameStr) - dk, err = db.keyer.GenerateDeviceFileKey(dk, folder, protocol.LocalDeviceID[:], name) - if err != nil { - return err - } - - ef, ok, err := t.getFileByKey(dk) - if err != nil { - return err - } - if !ok { - l.Debugf("remove (local); folder=%q %v: file doesn't exist", folder, nameStr) - continue - } - - buf, err = db.removeLocalBlockAndSequenceInfo(buf, folder, name, ef, true, &t) - if err != nil { - return err - } - - meta.removeFile(protocol.LocalDeviceID, ef) - - gk, err = db.keyer.GenerateGlobalVersionKey(gk, folder, name) - if err != nil { - return err - } - buf, err = t.removeFromGlobal(gk, buf, folder, protocol.LocalDeviceID[:], name, meta) - if err != nil { - return err - } - - err = t.Delete(dk) - if err != nil { - return err - } - - if err := t.Checkpoint(); err != nil { - return err - } - } - - return t.Commit() -} - -func (db *Lowlevel) removeLocalBlockAndSequenceInfo(keyBuf, folder, name []byte, ef protocol.FileInfo, removeFromBlockListMap bool, t *readWriteTransaction) ([]byte, error) { - var err error - if len(ef.Blocks) != 0 && !ef.IsInvalid() && ef.Size > 0 { - for _, block := range ef.Blocks { - keyBuf, err = db.keyer.GenerateBlockMapKey(keyBuf, folder, block.Hash, name) - if err != nil { - return nil, err - } - if err := t.Delete(keyBuf); err != nil { - return nil, err - } - } - if removeFromBlockListMap { - keyBuf, err := db.keyer.GenerateBlockListMapKey(keyBuf, folder, ef.BlocksHash, name) - if err != nil { - return nil, err - } - if err = t.Delete(keyBuf); err != nil { - return nil, err - } - } - } - - keyBuf, err = db.keyer.GenerateSequenceKey(keyBuf, folder, ef.SequenceNo()) - if err != nil { - return nil, err - } - if err := t.Delete(keyBuf); err != nil { - return nil, err - } - l.Debugf("removing sequence; folder=%q sequence=%v %v", folder, ef.SequenceNo(), ef.FileName()) - return keyBuf, nil -} - -func (db *Lowlevel) dropFolder(folder []byte) error { - db.gcMut.RLock() - defer db.gcMut.RUnlock() - - t, err := db.newReadWriteTransaction() - if err != nil { - return err - } - defer t.close() - - // Remove all items related to the given folder from the device->file bucket - k0, err := db.keyer.GenerateDeviceFileKey(nil, folder, nil, nil) - if err != nil { - return err - } - if err := t.deleteKeyPrefix(k0.WithoutNameAndDevice()); err != nil { - return err - } - - // Remove all sequences related to the folder - k1, err := db.keyer.GenerateSequenceKey(k0, folder, 0) - if err != nil { - return err - } - if err := t.deleteKeyPrefix(k1.WithoutSequence()); err != nil { - return err - } - - // Remove all items related to the given folder from the global bucket - k2, err := db.keyer.GenerateGlobalVersionKey(k1, folder, nil) - if err != nil { - return err - } - if err := t.deleteKeyPrefix(k2.WithoutName()); err != nil { - return err - } - - // Remove all needs related to the folder - k3, err := db.keyer.GenerateNeedFileKey(k2, folder, nil) - if err != nil { - return err - } - if err := t.deleteKeyPrefix(k3.WithoutName()); err != nil { - return err - } - - // Remove the blockmap of the folder - k4, err := db.keyer.GenerateBlockMapKey(k3, folder, nil, nil) - if err != nil { - return err - } - if err := t.deleteKeyPrefix(k4.WithoutHashAndName()); err != nil { - return err - } - - k5, err := db.keyer.GenerateBlockListMapKey(k4, folder, nil, nil) - if err != nil { - return err - } - if err := t.deleteKeyPrefix(k5.WithoutHashAndName()); err != nil { - return err - } - - return t.Commit() -} - -func (db *Lowlevel) dropDeviceFolder(device, folder []byte, meta *metadataTracker) error { - db.gcMut.RLock() - defer db.gcMut.RUnlock() - - t, err := db.newReadWriteTransaction(meta.CommitHook(folder)) - if err != nil { - return err - } - defer t.close() - - key, err := db.keyer.GenerateDeviceFileKey(nil, folder, device, nil) - if err != nil { - return err - } - dbi, err := t.NewPrefixIterator(key) - if err != nil { - return err - } - defer dbi.Release() - - var gk, keyBuf []byte - for dbi.Next() { - name := db.keyer.NameFromDeviceFileKey(dbi.Key()) - gk, err = db.keyer.GenerateGlobalVersionKey(gk, folder, name) - if err != nil { - return err - } - keyBuf, err = t.removeFromGlobal(gk, keyBuf, folder, device, name, meta) - if err != nil { - return err - } - if err := t.Delete(dbi.Key()); err != nil { - return err - } - if err := t.Checkpoint(); err != nil { - return err - } - } - dbi.Release() - if err := dbi.Error(); err != nil { - return err - } - - if bytes.Equal(device, protocol.LocalDeviceID[:]) { - key, err := db.keyer.GenerateBlockMapKey(nil, folder, nil, nil) - if err != nil { - return err - } - if err := t.deleteKeyPrefix(key.WithoutHashAndName()); err != nil { - return err - } - key2, err := db.keyer.GenerateBlockListMapKey(key, folder, nil, nil) - if err != nil { - return err - } - if err := t.deleteKeyPrefix(key2.WithoutHashAndName()); err != nil { - return err - } - } - return t.Commit() -} - -func (db *Lowlevel) checkGlobals(folderStr string) (int, error) { - t, err := db.newReadWriteTransaction() - if err != nil { - return 0, err - } - defer t.close() - - folder := []byte(folderStr) - key, err := db.keyer.GenerateGlobalVersionKey(nil, folder, nil) - if err != nil { - return 0, err - } - dbi, err := t.NewPrefixIterator(key.WithoutName()) - if err != nil { - return 0, err - } - defer dbi.Release() - - fixed := 0 - var dk []byte - ro := t.readOnlyTransaction - for dbi.Next() { - var vl dbproto.VersionList - if err := proto.Unmarshal(dbi.Value(), &vl); err != nil || len(vl.Versions) == 0 { - if err := t.Delete(dbi.Key()); err != nil && !backend.IsNotFound(err) { - return 0, err - } - continue - } - - // Check the global version list for consistency. An issue in previous - // versions of goleveldb could result in reordered writes so that - // there are global entries pointing to no longer existing files. Here - // we find those and clear them out. - - name := db.keyer.NameFromGlobalVersionKey(dbi.Key()) - newVL := &dbproto.VersionList{} - var changed, changedHere bool - for _, fv := range vl.Versions { - changedHere, err = checkGlobalsFilterDevices(dk, folder, name, fv.Devices, newVL, ro) - if err != nil { - return 0, err - } - changed = changed || changedHere - - changedHere, err = checkGlobalsFilterDevices(dk, folder, name, fv.InvalidDevices, newVL, ro) - if err != nil { - return 0, err - } - changed = changed || changedHere - } - - if len(newVL.Versions) == 0 { - if err := t.Delete(dbi.Key()); err != nil && !backend.IsNotFound(err) { - return 0, err - } - fixed++ - } else if changed { - if err := t.Put(dbi.Key(), mustMarshal(newVL)); err != nil { - return 0, err - } - fixed++ - } - } - dbi.Release() - if err := dbi.Error(); err != nil { - return 0, err - } - - l.Debugf("global db check completed for %v", folder) - return fixed, t.Commit() -} - -func checkGlobalsFilterDevices(dk, folder, name []byte, devices [][]byte, vl *dbproto.VersionList, t readOnlyTransaction) (bool, error) { - var changed bool - var err error - for _, device := range devices { - dk, err = t.keyer.GenerateDeviceFileKey(dk, folder, device, name) - if err != nil { - return false, err - } - f, ok, err := t.getFileTrunc(dk, false) - if err != nil { - return false, err - } - if !ok { - changed = true - continue - } - _, _, _, _, _, _, err = vlUpdate(vl, folder, device, f, t) - if err != nil { - return false, err - } - } - return changed, nil -} - -func (db *Lowlevel) getIndexID(device, folder []byte) (protocol.IndexID, error) { - key, err := db.keyer.GenerateIndexIDKey(nil, device, folder) - if err != nil { - return 0, err - } - cur, err := db.Get(key) - if backend.IsNotFound(err) { - return 0, nil - } else if err != nil { - return 0, err - } - - var id protocol.IndexID - if err := id.Unmarshal(cur); err != nil { - return 0, nil //nolint: nilerr - } - - return id, nil -} - -func (db *Lowlevel) setIndexID(device, folder []byte, id protocol.IndexID) error { - bs, _ := id.Marshal() // marshalling can't fail - key, err := db.keyer.GenerateIndexIDKey(nil, device, folder) - if err != nil { - return err - } - return db.Put(key, bs) -} - -func (db *Lowlevel) dropFolderIndexIDs(folder []byte) error { - t, err := db.newReadWriteTransaction() - if err != nil { - return err - } - defer t.close() - - if err := t.deleteKeyPrefixMatching([]byte{KeyTypeIndexID}, func(key []byte) bool { - keyFolder, ok := t.keyer.FolderFromIndexIDKey(key) - if !ok { - l.Debugf("Deleting IndexID with missing FolderIdx: %v", key) - return true - } - return bytes.Equal(keyFolder, folder) - }); err != nil { - return err - } - return t.Commit() -} - -func (db *Lowlevel) dropIndexIDs() error { - t, err := db.newReadWriteTransaction() - if err != nil { - return err - } - defer t.close() - if err := t.deleteKeyPrefix([]byte{KeyTypeIndexID}); err != nil { - return err - } - return t.Commit() -} - -// dropOtherDeviceIndexIDs drops all index IDs for devices other than the -// local device. This means we will resend our indexes to all other devices, -// but they don't have to resend to us. -func (db *Lowlevel) dropOtherDeviceIndexIDs() error { - t, err := db.newReadWriteTransaction() - if err != nil { - return err - } - defer t.close() - if err := t.deleteKeyPrefixMatching([]byte{KeyTypeIndexID}, func(key []byte) bool { - dev, _ := t.keyer.DeviceFromIndexIDKey(key) - return !bytes.Equal(dev, protocol.LocalDeviceID[:]) - }); err != nil { - return err - } - return t.Commit() -} - -func (db *Lowlevel) dropMtimes(folder []byte) error { - key, err := db.keyer.GenerateMtimesKey(nil, folder) - if err != nil { - return err - } - return db.dropPrefix(key) -} - -func (db *Lowlevel) dropFolderMeta(folder []byte) error { - key, err := db.keyer.GenerateFolderMetaKey(nil, folder) - if err != nil { - return err - } - return db.dropPrefix(key) -} - -func (db *Lowlevel) dropPrefix(prefix []byte) error { - t, err := db.newReadWriteTransaction() - if err != nil { - return err - } - defer t.close() - - if err := t.deleteKeyPrefix(prefix); err != nil { - return err - } - return t.Commit() -} - -func (db *Lowlevel) gcRunner(ctx context.Context) error { - // Calculate the time for the next GC run. Even if we should run GC - // directly, give the system a while to get up and running and do other - // stuff first. (We might have migrations and stuff which would be - // better off running before GC.) - next := db.timeUntil(indirectGCTimeKey, db.indirectGCInterval) - if next < time.Minute { - next = time.Minute - } - - t := time.NewTimer(next) - defer t.Stop() - - for { - select { - case <-ctx.Done(): - return ctx.Err() - case <-t.C: - if err := db.gcIndirect(ctx); err != nil { - l.Warnln("Database indirection GC failed:", err) - } - db.recordTime(indirectGCTimeKey) - t.Reset(db.timeUntil(indirectGCTimeKey, db.indirectGCInterval)) - } - } -} - -// recordTime records the current time under the given key, affecting the -// next call to timeUntil with the same key. -func (db *Lowlevel) recordTime(key string) { - miscDB := NewMiscDataNamespace(db) - _ = miscDB.PutInt64(key, time.Now().Unix()) // error wilfully ignored -} - -// timeUntil returns how long we should wait until the next interval, or -// zero if it should happen directly. -func (db *Lowlevel) timeUntil(key string, every time.Duration) time.Duration { - miscDB := NewMiscDataNamespace(db) - lastTime, _, _ := miscDB.Int64(key) // error wilfully ignored - nextTime := time.Unix(lastTime, 0).Add(every) - sleepTime := time.Until(nextTime) - if sleepTime < 0 { - sleepTime = 0 - } - return sleepTime -} - -func (db *Lowlevel) gcIndirect(ctx context.Context) (err error) { - // The indirection GC uses bloom filters to track used block lists and - // versions. This means iterating over all items, adding their hashes to - // the filter, then iterating over the indirected items and removing - // those that don't match the filter. The filter will give false - // positives so we will keep around one percent of things that we don't - // really need (at most). - // - // Indirection GC needs to run when there are no modifications to the - // FileInfos or indirected items. - - l.Debugln("Starting database GC") - - // Create a new set of bloom filters, while holding the gcMut which - // guarantees that no other modifications are happening concurrently. - - db.gcMut.Lock() - capacity := indirectGCBloomCapacity - if db.gcKeyCount > capacity { - capacity = db.gcKeyCount - } - db.blockFilter = newBloomFilter(capacity) - db.versionFilter = newBloomFilter(capacity) - db.gcMut.Unlock() - - defer func() { - // Forget the bloom filters on the way out. - db.gcMut.Lock() - db.blockFilter = nil - db.versionFilter = nil - db.gcMut.Unlock() - }() - - var discardedBlocks, matchedBlocks, discardedVersions, matchedVersions int - - t, err := db.newReadWriteTransaction() - if err != nil { - return err - } - defer t.Release() - - // Set up the bloom filters with the initial capacity and false positive - // rate, or higher capacity if we've done this before and seen lots of - // items. For simplicity's sake we track just one count, which is the - // highest of the various indirected items. - - // Iterate the FileInfos, unmarshal the block and version hashes and - // add them to the filter. - - // This happens concurrently with normal database modifications, though - // those modifications will now also add their blocks and versions to - // the bloom filters. - - it, err := t.NewPrefixIterator([]byte{KeyTypeDevice}) - if err != nil { - return err - } - defer it.Release() - for it.Next() { - select { - case <-ctx.Done(): - return ctx.Err() - default: - } - - var hashes dbproto.IndirectionHashesOnly - if err := proto.Unmarshal(it.Value(), &hashes); err != nil { - return err - } - db.recordIndirectionHashes(&hashes) - } - it.Release() - if err := it.Error(); err != nil { - return err - } - - // For the next phase we grab the GC lock again and hold it for the rest - // of the method call. Now there can't be any further modifications to - // the database or the bloom filters. - - db.gcMut.Lock() - defer db.gcMut.Unlock() - - // Only print something if the process takes more than "a moment". - logWait := make(chan struct{}) - logTimer := time.AfterFunc(10*time.Second, func() { - l.Infoln("Database GC in progress - many Syncthing operations will be unresponsive until it's finished") - close(logWait) - }) - defer func() { - if logTimer.Stop() { - return - } - <-logWait // Make sure messages are sent in order. - l.Infof("Database GC complete (discarded/remaining: %v/%v blocks, %v/%v versions)", - discardedBlocks, matchedBlocks, discardedVersions, matchedVersions) - }() - - // Iterate over block lists, removing keys with hashes that don't match - // the filter. - - it, err = t.NewPrefixIterator([]byte{KeyTypeBlockList}) - if err != nil { - return err - } - defer it.Release() - for it.Next() { - select { - case <-ctx.Done(): - return ctx.Err() - default: - } - - key := blockListKey(it.Key()) - if db.blockFilter.has(key.Hash()) { - matchedBlocks++ - continue - } - if err := t.Delete(key); err != nil { - return err - } - discardedBlocks++ - } - it.Release() - if err := it.Error(); err != nil { - return err - } - - // Iterate over version lists, removing keys with hashes that don't match - // the filter. - - it, err = db.NewPrefixIterator([]byte{KeyTypeVersion}) - if err != nil { - return err - } - for it.Next() { - select { - case <-ctx.Done(): - return ctx.Err() - default: - } - - key := versionKey(it.Key()) - if db.versionFilter.has(key.Hash()) { - matchedVersions++ - continue - } - if err := t.Delete(key); err != nil { - return err - } - discardedVersions++ - } - it.Release() - if err := it.Error(); err != nil { - return err - } - - // Remember the number of unique keys we kept until the next pass. - db.gcKeyCount = matchedBlocks - if matchedVersions > matchedBlocks { - db.gcKeyCount = matchedVersions - } - - if err := t.Commit(); err != nil { - return err - } - - l.Debugf("Finished GC (discarded/remaining: %v/%v blocks, %v/%v versions)", discardedBlocks, matchedBlocks, discardedVersions, matchedVersions) - - return nil -} - -func (db *Lowlevel) recordIndirectionHashesForFile(f *protocol.FileInfo) { - db.recordIndirectionHashes(&dbproto.IndirectionHashesOnly{BlocksHash: f.BlocksHash, VersionHash: f.VersionHash}) -} - -func (db *Lowlevel) recordIndirectionHashes(hs *dbproto.IndirectionHashesOnly) { - // must be called with gcMut held (at least read-held) - if db.blockFilter != nil && len(hs.BlocksHash) > 0 { - db.blockFilter.add(hs.BlocksHash) - } - if db.versionFilter != nil && len(hs.VersionHash) > 0 { - db.versionFilter.add(hs.VersionHash) - } -} - -func newBloomFilter(capacity int) *bloomFilter { - return &bloomFilter{ - f: blobloom.NewSyncOptimized(blobloom.Config{ - Capacity: uint64(capacity), - FPRate: indirectGCBloomFalsePositiveRate, - MaxBits: 8 * indirectGCBloomMaxBytes, - }), - seed: maphash.MakeSeed(), - } -} - -type bloomFilter struct { - f *blobloom.SyncFilter - seed maphash.Seed -} - -func (b *bloomFilter) add(id []byte) { b.f.Add(b.hash(id)) } -func (b *bloomFilter) has(id []byte) bool { return b.f.Has(b.hash(id)) } - -// Hash function for the bloomfilter: maphash of the SHA-256. -// -// The randomization in maphash should ensure that we get different collisions -// across runs, so colliding keys are not kept indefinitely. -func (b *bloomFilter) hash(id []byte) uint64 { - if len(id) != sha256.Size { - panic("bug: bloomFilter.hash passed something not a SHA256 hash") - } - var h maphash.Hash - h.SetSeed(b.seed) - _, _ = h.Write(id) - return h.Sum64() -} - -// checkRepair checks folder metadata and sequences for miscellaneous errors. -func (db *Lowlevel) checkRepair() error { - db.gcMut.RLock() - defer db.gcMut.RUnlock() - for _, folder := range db.ListFolders() { - if _, err := db.getMetaAndCheckGCLocked(folder); err != nil { - return err - } - } - return nil -} - -func (db *Lowlevel) getMetaAndCheck(folder string) (*metadataTracker, error) { - db.gcMut.RLock() - defer db.gcMut.RUnlock() - - return db.getMetaAndCheckGCLocked(folder) -} - -func (db *Lowlevel) getMetaAndCheckGCLocked(folder string) (*metadataTracker, error) { - fixed, err := db.checkLocalNeed([]byte(folder)) - if err != nil { - return nil, fmt.Errorf("checking local need: %w", err) - } - if fixed != 0 { - l.Infof("Repaired %d local need entries for folder %v in database", fixed, folder) - } - - fixed, err = db.checkGlobals(folder) - if err != nil { - return nil, fmt.Errorf("checking globals: %w", err) - } - if fixed != 0 { - l.Infof("Repaired %d global entries for folder %v in database", fixed, folder) - } - - oldMeta := newMetadataTracker(db.keyer, db.evLogger) - _ = oldMeta.fromDB(db, []byte(folder)) // Ignore error, it leads to index id reset too - meta, err := db.recalcMeta(folder) - if err != nil { - return nil, fmt.Errorf("recalculating metadata: %w", err) - } - - fixed, err = db.repairSequenceGCLocked(folder, meta) - if err != nil { - return nil, fmt.Errorf("repairing sequences: %w", err) - } - if fixed != 0 { - l.Infof("Repaired %d sequence entries for folder %v in database", fixed, folder) - meta, err = db.recalcMeta(folder) - if err != nil { - return nil, fmt.Errorf("recalculating metadata: %w", err) - } - } - - if err := db.checkSequencesUnchanged(folder, oldMeta, meta); err != nil { - return nil, fmt.Errorf("checking for changed sequences: %w", err) - } - - return meta, nil -} - -func (db *Lowlevel) loadMetadataTracker(folder string) (*metadataTracker, error) { - meta := newMetadataTracker(db.keyer, db.evLogger) - if err := meta.fromDB(db, []byte(folder)); err != nil { - if errors.Is(err, errMetaInconsistent) { - l.Infof("Stored folder metadata for %q is inconsistent; recalculating", folder) - } else { - l.Infof("No stored folder metadata for %q; recalculating", folder) - } - return db.getMetaAndCheck(folder) - } - - curSeq := meta.Sequence(protocol.LocalDeviceID) - if metaOK, err := db.verifyLocalSequence(curSeq, folder); err != nil { - return nil, fmt.Errorf("verifying sequences: %w", err) - } else if !metaOK { - l.Infof("Stored folder metadata for %q is out of date after crash; recalculating", folder) - return db.getMetaAndCheck(folder) - } - - if age := time.Since(meta.Created()); age > db.recheckInterval { - l.Infof("Stored folder metadata for %q is %v old; recalculating", folder, stringutil.NiceDurationString(age)) - return db.getMetaAndCheck(folder) - } - - return meta, nil -} - -func (db *Lowlevel) recalcMeta(folderStr string) (*metadataTracker, error) { - folder := []byte(folderStr) - - meta := newMetadataTracker(db.keyer, db.evLogger) - - t, err := db.newReadWriteTransaction(meta.CommitHook(folder)) - if err != nil { - return nil, err - } - defer t.close() - - var deviceID protocol.DeviceID - err = t.withAllFolderTruncated(folder, func(device []byte, f protocol.FileInfo) bool { - copy(deviceID[:], device) - meta.addFile(deviceID, f) - return true - }) - if err != nil { - return nil, err - } - - err = t.withGlobal(folder, nil, true, func(f protocol.FileInfo) bool { - meta.addFile(protocol.GlobalDeviceID, f) - return true - }) - if err != nil { - return nil, err - } - - meta.emptyNeeded(protocol.LocalDeviceID) - err = t.withNeed(folder, protocol.LocalDeviceID[:], true, func(f protocol.FileInfo) bool { - meta.addNeeded(protocol.LocalDeviceID, f) - return true - }) - if err != nil { - return nil, err - } - for _, device := range meta.devices() { - meta.emptyNeeded(device) - err = t.withNeed(folder, device[:], true, func(f protocol.FileInfo) bool { - meta.addNeeded(device, f) - return true - }) - if err != nil { - return nil, err - } - } - - meta.SetCreated() - if err := t.Commit(); err != nil { - return nil, err - } - return meta, nil -} - -// Verify the local sequence number from actual sequence entries. Returns -// true if it was all good, or false if a fixup was necessary. -func (db *Lowlevel) verifyLocalSequence(curSeq int64, folder string) (bool, error) { - // Walk the sequence index from the current (supposedly) highest - // sequence number and raise the alarm if we get anything. This recovers - // from the occasion where we have written sequence entries to disk but - // not yet written new metadata to disk. - // - // Note that we can have the same thing happen for remote devices but - // there it's not a problem -- we'll simply advertise a lower sequence - // number than we've actually seen and receive some duplicate updates - // and then be in sync again. - - t, err := db.newReadOnlyTransaction() - if err != nil { - return false, err - } - ok := true - if err := t.withHaveSequence([]byte(folder), curSeq+1, func(_ protocol.FileInfo) bool { - ok = false // we got something, which we should not have - return false - }); err != nil { - return false, err - } - t.close() - - return ok, nil -} - -// repairSequenceGCLocked makes sure the sequence numbers in the sequence keys -// match those in the corresponding file entries. It returns the amount of fixed -// entries. -func (db *Lowlevel) repairSequenceGCLocked(folderStr string, meta *metadataTracker) (int, error) { - t, err := db.newReadWriteTransaction(meta.CommitHook([]byte(folderStr))) - if err != nil { - return 0, err - } - defer t.close() - - fixed := 0 - - folder := []byte(folderStr) - - // First check that every file entry has a matching sequence entry - // (this was previously db schema upgrade to 9). - - dk, err := t.keyer.GenerateDeviceFileKey(nil, folder, protocol.LocalDeviceID[:], nil) - if err != nil { - return 0, err - } - it, err := t.NewPrefixIterator(dk.WithoutName()) - if err != nil { - return 0, err - } - defer it.Release() - - var sk sequenceKey - for it.Next() { - intf, err := t.unmarshalTrunc(it.Value(), false) - if err != nil { - // Delete local items with invalid indirected blocks/versions. - // They will be rescanned. - var ierr *blocksIndirectionError - if ok := errors.As(err, &ierr); ok && backend.IsNotFound(err) { - intf, err = t.unmarshalTrunc(it.Value(), true) - if err != nil { - return 0, err - } - name := []byte(intf.FileName()) - gk, err := t.keyer.GenerateGlobalVersionKey(nil, folder, name) - if err != nil { - return 0, err - } - _, err = t.removeFromGlobal(gk, nil, folder, protocol.LocalDeviceID[:], name, nil) - if err != nil { - return 0, err - } - sk, err = db.keyer.GenerateSequenceKey(sk, folder, intf.SequenceNo()) - if err != nil { - return 0, err - } - if err := t.Delete(sk); err != nil { - return 0, err - } - if err := t.Delete(it.Key()); err != nil { - return 0, err - } - } - return 0, err - } - if sk, err = t.keyer.GenerateSequenceKey(sk, folder, intf.Sequence); err != nil { - return 0, err - } - switch dk, err = t.Get(sk); { - case err != nil: - if !backend.IsNotFound(err) { - return 0, err - } - fallthrough - case !bytes.Equal(it.Key(), dk): - fixed++ - intf.Sequence = meta.nextLocalSeq() - if sk, err = t.keyer.GenerateSequenceKey(sk, folder, intf.Sequence); err != nil { - return 0, err - } - if err := t.Put(sk, it.Key()); err != nil { - return 0, err - } - if err := t.putFile(it.Key(), intf); err != nil { - return 0, err - } - } - if err := t.Checkpoint(); err != nil { - return 0, err - } - } - if err := it.Error(); err != nil { - return 0, err - } - - it.Release() - - // Secondly check there's no sequence entries pointing at incorrect things. - - sk, err = t.keyer.GenerateSequenceKey(sk, folder, 0) - if err != nil { - return 0, err - } - - it, err = t.NewPrefixIterator(sk.WithoutSequence()) - if err != nil { - return 0, err - } - defer it.Release() - - for it.Next() { - // Check that the sequence from the key matches the - // sequence in the file. - fi, ok, err := t.getFileTrunc(it.Value(), true) - if err != nil { - return 0, err - } - if ok { - if seq := t.keyer.SequenceFromSequenceKey(it.Key()); seq == fi.SequenceNo() { - continue - } - } - // Either the file is missing or has a different sequence number - fixed++ - if err := t.Delete(it.Key()); err != nil { - return 0, err - } - } - if err := it.Error(); err != nil { - return 0, err - } - - it.Release() - - return fixed, t.Commit() -} - -// Does not take care of metadata - if anything is repaired, the need count -// needs to be recalculated. -func (db *Lowlevel) checkLocalNeed(folder []byte) (int, error) { - repaired := 0 - - t, err := db.newReadWriteTransaction() - if err != nil { - return 0, err - } - defer t.close() - - key, err := t.keyer.GenerateNeedFileKey(nil, folder, nil) - if err != nil { - return 0, err - } - dbi, err := t.NewPrefixIterator(key.WithoutName()) - if err != nil { - return 0, err - } - defer dbi.Release() - - var needName string - var needDone bool - next := func() { - needDone = !dbi.Next() - if !needDone { - needName = string(t.keyer.NameFromGlobalVersionKey(dbi.Key())) - } - } - next() - itErr := t.withNeedIteratingGlobal(folder, protocol.LocalDeviceID[:], true, func(fi protocol.FileInfo) bool { - for !needDone && needName < fi.Name { - repaired++ - if err = t.Delete(dbi.Key()); err != nil && !backend.IsNotFound(err) { - return false - } - l.Debugln("check local need: removing", needName) - next() - } - if needName == fi.Name { - next() - } else { - repaired++ - key, err = t.keyer.GenerateNeedFileKey(key, folder, []byte(fi.Name)) - if err != nil { - return false - } - if err = t.Put(key, nil); err != nil { - return false - } - l.Debugln("check local need: adding", fi.Name) - } - return true - }) - if err != nil { - return 0, err - } - if itErr != nil { - return 0, itErr - } - - for !needDone { - repaired++ - if err := t.Delete(dbi.Key()); err != nil && !backend.IsNotFound(err) { - return 0, err - } - l.Debugln("check local need: removing", needName) - next() - } - - if err := dbi.Error(); err != nil { - return 0, err - } - dbi.Release() - - if err = t.Commit(); err != nil { - return 0, err - } - - return repaired, nil -} - -// checkSequencesUnchanged resets delta indexes for any device where the -// sequence changed. -func (db *Lowlevel) checkSequencesUnchanged(folder string, oldMeta, meta *metadataTracker) error { - t, err := db.newReadWriteTransaction() - if err != nil { - return err - } - defer t.close() - - var key []byte - deleteIndexID := func(devID protocol.DeviceID) error { - key, err = db.keyer.GenerateIndexIDKey(key, devID[:], []byte(folder)) - if err != nil { - return err - } - return t.Delete(key) - } - - if oldMeta.Sequence(protocol.LocalDeviceID) != meta.Sequence(protocol.LocalDeviceID) { - if err := deleteIndexID(protocol.LocalDeviceID); err != nil { - return err - } - l.Infof("Local sequence for folder %v changed while repairing - dropping delta indexes", folder) - } - - oldDevices := oldMeta.devices() - oldSequences := make(map[protocol.DeviceID]int64, len(oldDevices)) - for _, devID := range oldDevices { - oldSequences[devID] = oldMeta.Sequence(devID) - } - for _, devID := range meta.devices() { - oldSeq := oldSequences[devID] - delete(oldSequences, devID) - // A lower sequence number just means we will receive some indexes again. - if oldSeq >= meta.Sequence(devID) { - if oldSeq > meta.Sequence(devID) { - db.evLogger.Log(events.Failure, "lower remote sequence after recalculating metadata") - } - continue - } - db.evLogger.Log(events.Failure, "higher remote sequence after recalculating metadata") - if err := deleteIndexID(devID); err != nil { - return err - } - l.Infof("Sequence of device %v for folder %v changed while repairing - dropping delta indexes", devID.Short(), folder) - } - for devID := range oldSequences { - if err := deleteIndexID(devID); err != nil { - return err - } - l.Debugf("Removed indexID of device %v for folder %v which isn't present anymore", devID.Short(), folder) - } - - return t.Commit() -} - -func (db *Lowlevel) needsRepairPath() string { - path := db.Location() - if path == "" { - return "" - } - if path[len(path)-1] == fs.PathSeparator { - path = path[:len(path)-1] - } - return path + needsRepairSuffix -} - -func (db *Lowlevel) checkErrorForRepair(err error) { - if errors.Is(err, errEntryFromGlobalMissing) || errors.Is(err, errEmptyGlobal) { - // Inconsistency error, mark db for repair on next start. - if path := db.needsRepairPath(); path != "" { - if fd, err := os.Create(path); err == nil { - fd.Close() - } - } - } -} - -func (db *Lowlevel) handleFailure(err error) { - db.checkErrorForRepair(err) - if shouldReportFailure(err) { - db.evLogger.Log(events.Failure, err.Error()) - } -} - -var ldbPathRe = regexp.MustCompile(`(open|write|read) .+[\\/].+[\\/]index[^\\/]+[\\/][^\\/]+: `) - -func shouldReportFailure(err error) bool { - return !ldbPathRe.MatchString(err.Error()) -} diff --git a/lib/db/meta.go b/lib/db/meta.go deleted file mode 100644 index fd7ec1fce..000000000 --- a/lib/db/meta.go +++ /dev/null @@ -1,472 +0,0 @@ -// Copyright (C) 2017 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package db - -import ( - "errors" - "fmt" - "math/bits" - "time" - - "google.golang.org/protobuf/proto" - - "github.com/syncthing/syncthing/internal/gen/dbproto" - "github.com/syncthing/syncthing/lib/db/backend" - "github.com/syncthing/syncthing/lib/events" - "github.com/syncthing/syncthing/lib/protocol" - "github.com/syncthing/syncthing/lib/sync" -) - -var errMetaInconsistent = errors.New("inconsistent counts detected") - -type countsMap struct { - counts CountsSet - indexes map[metaKey]int // device ID + local flags -> index in counts -} - -// metadataTracker keeps metadata on a per device, per local flag basis. -type metadataTracker struct { - keyer keyer - countsMap - mut sync.RWMutex - dirty bool - evLogger events.Logger -} - -type metaKey struct { - dev protocol.DeviceID - flag uint32 -} - -const needFlag uint32 = 1 << 31 // Last bit, as early ones are local flags - -func newMetadataTracker(keyer keyer, evLogger events.Logger) *metadataTracker { - return &metadataTracker{ - keyer: keyer, - mut: sync.NewRWMutex(), - countsMap: countsMap{ - indexes: make(map[metaKey]int), - }, - evLogger: evLogger, - } -} - -// Unmarshal loads a metadataTracker from the corresponding protobuf -// representation -func (m *metadataTracker) Unmarshal(bs []byte) error { - var dbc dbproto.CountsSet - if err := proto.Unmarshal(bs, &dbc); err != nil { - return err - } - m.counts.Created = dbc.Created - m.counts.Counts = make([]Counts, len(dbc.Counts)) - for i, c := range dbc.Counts { - m.counts.Counts[i] = countsFromWire(c) - } - - // Initialize the index map - m.indexes = make(map[metaKey]int) - for i, c := range m.counts.Counts { - m.indexes[metaKey{c.DeviceID, c.LocalFlags}] = i - } - return nil -} - -// protoMarshal returns the protobuf representation of the metadataTracker. -// Must be called with the read lock held. -func (m *metadataTracker) protoMarshal() ([]byte, error) { - dbc := &dbproto.CountsSet{ - Counts: make([]*dbproto.Counts, len(m.counts.Counts)), - Created: m.counts.Created, - } - for i, c := range m.counts.Counts { - dbc.Counts[i] = c.toWire() - } - return proto.Marshal(dbc) -} - -func (m *metadataTracker) CommitHook(folder []byte) backend.CommitHook { - return func(t backend.WriteTransaction) error { - return m.toDB(t, folder) - } -} - -// toDB saves the marshalled metadataTracker to the given db, under the key -// corresponding to the given folder -func (m *metadataTracker) toDB(t backend.WriteTransaction, folder []byte) error { - key, err := m.keyer.GenerateFolderMetaKey(nil, folder) - if err != nil { - return err - } - - m.mut.RLock() - defer m.mut.RUnlock() - - if !m.dirty { - return nil - } - - bs, err := m.protoMarshal() - if err != nil { - return err - } - err = t.Put(key, bs) - if err == nil { - m.dirty = false - } - - return err -} - -// fromDB initializes the metadataTracker from the marshalled data found in -// the database under the key corresponding to the given folder -func (m *metadataTracker) fromDB(db *Lowlevel, folder []byte) error { - key, err := db.keyer.GenerateFolderMetaKey(nil, folder) - if err != nil { - return err - } - bs, err := db.Get(key) - if err != nil { - return err - } - if err = m.Unmarshal(bs); err != nil { - return err - } - if m.counts.Created == 0 { - return errMetaInconsistent - } - return nil -} - -// countsPtr returns a pointer to the corresponding Counts struct, if -// necessary allocating one in the process -func (m *metadataTracker) countsPtr(dev protocol.DeviceID, flag uint32) *Counts { - // must be called with the mutex held - - if bits.OnesCount32(flag) > 1 { - panic("incorrect usage: set at most one bit in flag") - } - - key := metaKey{dev, flag} - idx, ok := m.indexes[key] - if !ok { - idx = len(m.counts.Counts) - m.counts.Counts = append(m.counts.Counts, Counts{DeviceID: dev, LocalFlags: flag}) - m.indexes[key] = idx - // Need bucket must be initialized when a device first occurs in - // the metadatatracker, even if there's no change to the need - // bucket itself. - nkey := metaKey{dev, needFlag} - if _, ok := m.indexes[nkey]; !ok { - // Initially a new device needs everything, except deletes - nidx := len(m.counts.Counts) - m.counts.Counts = append(m.counts.Counts, m.allNeededCounts(dev)) - m.indexes[nkey] = nidx - } - } - return &m.counts.Counts[idx] -} - -// allNeeded makes sure there is a counts in case the device needs everything. -func (m *countsMap) allNeededCounts(dev protocol.DeviceID) Counts { - var counts Counts - if idx, ok := m.indexes[metaKey{protocol.GlobalDeviceID, 0}]; ok { - counts = m.counts.Counts[idx] - counts.Deleted = 0 // Don't need deletes if having nothing - } - counts.DeviceID = dev - counts.LocalFlags = needFlag - return counts -} - -// addFile adds a file to the counts, adjusting the sequence number as -// appropriate -func (m *metadataTracker) addFile(dev protocol.DeviceID, f protocol.FileInfo) { - m.mut.Lock() - defer m.mut.Unlock() - - m.updateSeqLocked(dev, f) - - m.updateFileLocked(dev, f, m.addFileLocked) -} - -func (m *metadataTracker) updateFileLocked(dev protocol.DeviceID, f protocol.FileInfo, fn func(protocol.DeviceID, uint32, protocol.FileInfo)) { - m.dirty = true - - if f.IsInvalid() && (f.FileLocalFlags() == 0 || dev == protocol.GlobalDeviceID) { - // This is a remote invalid file or concern the global state. - // In either case invalid files are not accounted. - return - } - - if flags := f.FileLocalFlags(); flags == 0 { - // Account regular files in the zero-flags bucket. - fn(dev, 0, f) - } else { - // Account in flag specific buckets. - eachFlagBit(flags, func(flag uint32) { - fn(dev, flag, f) - }) - } -} - -// emptyNeeded ensures that there is a need count for the given device and that it is empty. -func (m *metadataTracker) emptyNeeded(dev protocol.DeviceID) { - m.mut.Lock() - defer m.mut.Unlock() - - m.dirty = true - - empty := Counts{ - DeviceID: dev, - LocalFlags: needFlag, - } - key := metaKey{dev, needFlag} - if idx, ok := m.indexes[key]; ok { - m.counts.Counts[idx] = empty - return - } - m.indexes[key] = len(m.counts.Counts) - m.counts.Counts = append(m.counts.Counts, empty) -} - -// addNeeded adds a file to the needed counts -func (m *metadataTracker) addNeeded(dev protocol.DeviceID, f protocol.FileInfo) { - m.mut.Lock() - defer m.mut.Unlock() - - m.dirty = true - - m.addFileLocked(dev, needFlag, f) -} - -func (m *metadataTracker) Sequence(dev protocol.DeviceID) int64 { - m.mut.Lock() - defer m.mut.Unlock() - return m.countsPtr(dev, 0).Sequence -} - -func (m *metadataTracker) updateSeqLocked(dev protocol.DeviceID, f protocol.FileInfo) { - if dev == protocol.GlobalDeviceID { - return - } - if cp := m.countsPtr(dev, 0); f.SequenceNo() > cp.Sequence { - cp.Sequence = f.SequenceNo() - } -} - -func (m *metadataTracker) addFileLocked(dev protocol.DeviceID, flag uint32, f protocol.FileInfo) { - cp := m.countsPtr(dev, flag) - - switch { - case f.IsDeleted(): - cp.Deleted++ - case f.IsDirectory() && !f.IsSymlink(): - cp.Directories++ - case f.IsSymlink(): - cp.Symlinks++ - default: - cp.Files++ - } - cp.Bytes += f.FileSize() -} - -// removeFile removes a file from the counts -func (m *metadataTracker) removeFile(dev protocol.DeviceID, f protocol.FileInfo) { - m.mut.Lock() - defer m.mut.Unlock() - - m.updateFileLocked(dev, f, m.removeFileLocked) -} - -// removeNeeded removes a file from the needed counts -func (m *metadataTracker) removeNeeded(dev protocol.DeviceID, f protocol.FileInfo) { - m.mut.Lock() - defer m.mut.Unlock() - - m.dirty = true - - m.removeFileLocked(dev, needFlag, f) -} - -func (m *metadataTracker) removeFileLocked(dev protocol.DeviceID, flag uint32, f protocol.FileInfo) { - cp := m.countsPtr(dev, flag) - - switch { - case f.IsDeleted(): - cp.Deleted-- - case f.IsDirectory() && !f.IsSymlink(): - cp.Directories-- - case f.IsSymlink(): - cp.Symlinks-- - default: - cp.Files-- - } - cp.Bytes -= f.FileSize() - - // If we've run into an impossible situation, correct it for now and set - // the created timestamp to zero. Next time we start up the metadata - // will be seen as infinitely old and recalculated from scratch. - if cp.Deleted < 0 { - m.evLogger.Log(events.Failure, fmt.Sprintf("meta deleted count for flag 0x%x dropped below zero", flag)) - cp.Deleted = 0 - m.counts.Created = 0 - } - if cp.Files < 0 { - m.evLogger.Log(events.Failure, fmt.Sprintf("meta files count for flag 0x%x dropped below zero", flag)) - cp.Files = 0 - m.counts.Created = 0 - } - if cp.Directories < 0 { - m.evLogger.Log(events.Failure, fmt.Sprintf("meta directories count for flag 0x%x dropped below zero", flag)) - cp.Directories = 0 - m.counts.Created = 0 - } - if cp.Symlinks < 0 { - m.evLogger.Log(events.Failure, fmt.Sprintf("meta deleted count for flag 0x%x dropped below zero", flag)) - cp.Symlinks = 0 - m.counts.Created = 0 - } -} - -// resetAll resets all metadata for the given device -func (m *metadataTracker) resetAll(dev protocol.DeviceID) { - m.mut.Lock() - m.dirty = true - for i, c := range m.counts.Counts { - if c.DeviceID == dev { - if c.LocalFlags != needFlag { - m.counts.Counts[i] = Counts{ - DeviceID: c.DeviceID, - LocalFlags: c.LocalFlags, - } - } else { - m.counts.Counts[i] = m.allNeededCounts(dev) - } - } - } - m.mut.Unlock() -} - -// resetCounts resets the file, dir, etc. counters, while retaining the -// sequence number -func (m *metadataTracker) resetCounts(dev protocol.DeviceID) { - m.mut.Lock() - m.dirty = true - - for i, c := range m.counts.Counts { - if c.DeviceID == dev { - m.counts.Counts[i] = Counts{ - DeviceID: c.DeviceID, - Sequence: c.Sequence, - LocalFlags: c.LocalFlags, - } - } - } - - m.mut.Unlock() -} - -func (m *countsMap) Counts(dev protocol.DeviceID, flag uint32) Counts { - if bits.OnesCount32(flag) > 1 { - panic("incorrect usage: set at most one bit in flag") - } - - idx, ok := m.indexes[metaKey{dev, flag}] - if !ok { - if flag == needFlag { - // If there's nothing about a device in the index yet, - // it needs everything. - return m.allNeededCounts(dev) - } - return Counts{} - } - - return m.counts.Counts[idx] -} - -// Snapshot returns a copy of the metadata for reading. -func (m *metadataTracker) Snapshot() *countsMap { - m.mut.RLock() - defer m.mut.RUnlock() - - c := &countsMap{ - counts: CountsSet{ - Counts: make([]Counts, len(m.counts.Counts)), - Created: m.counts.Created, - }, - indexes: make(map[metaKey]int, len(m.indexes)), - } - for k, v := range m.indexes { - c.indexes[k] = v - } - copy(c.counts.Counts, m.counts.Counts) - - return c -} - -// nextLocalSeq allocates a new local sequence number -func (m *metadataTracker) nextLocalSeq() int64 { - m.mut.Lock() - defer m.mut.Unlock() - - c := m.countsPtr(protocol.LocalDeviceID, 0) - c.Sequence++ - return c.Sequence -} - -// devices returns the list of devices tracked, excluding the local device -// (which we don't know the ID of) -func (m *metadataTracker) devices() []protocol.DeviceID { - m.mut.RLock() - defer m.mut.RUnlock() - return m.countsMap.devices() -} - -func (m *countsMap) devices() []protocol.DeviceID { - devs := make([]protocol.DeviceID, 0, len(m.counts.Counts)) - - for _, dev := range m.counts.Counts { - if dev.Sequence > 0 { - if dev.DeviceID == protocol.GlobalDeviceID || dev.DeviceID == protocol.LocalDeviceID { - continue - } - devs = append(devs, dev.DeviceID) - } - } - - return devs -} - -func (m *metadataTracker) Created() time.Time { - m.mut.RLock() - defer m.mut.RUnlock() - return time.Unix(0, m.counts.Created) -} - -func (m *metadataTracker) SetCreated() { - m.mut.Lock() - m.counts.Created = time.Now().UnixNano() - m.dirty = true - m.mut.Unlock() -} - -// eachFlagBit calls the function once for every bit that is set in flags -func eachFlagBit(flags uint32, fn func(flag uint32)) { - // Test each bit from the right, as long as there are bits left in the - // flag set. Clear any bits found and stop testing as soon as there are - // no more bits set. - - currentBit := uint32(1 << 0) - for flags != 0 { - if flags¤tBit != 0 { - fn(currentBit) - flags &^= currentBit - } - currentBit <<= 1 - } -} diff --git a/lib/db/meta_test.go b/lib/db/meta_test.go deleted file mode 100644 index e56e9302a..000000000 --- a/lib/db/meta_test.go +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright (C) 2018 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package db - -import ( - "math/bits" - "slices" - "testing" - - "github.com/syncthing/syncthing/lib/events" - "github.com/syncthing/syncthing/lib/protocol" -) - -func TestEachFlagBit(t *testing.T) { - cases := []struct { - flags uint32 - iterations int - }{ - {0, 0}, - {1<<0 | 1<<3, 2}, - {1 << 0, 1}, - {1 << 31, 1}, - {1<<10 | 1<<20 | 1<<30, 3}, - } - - for _, tc := range cases { - var flags uint32 - iterations := 0 - - eachFlagBit(tc.flags, func(f uint32) { - iterations++ - flags |= f - if bits.OnesCount32(f) != 1 { - t.Error("expected exactly one bit to be set in every call") - } - }) - - if flags != tc.flags { - t.Errorf("expected 0x%x flags, got 0x%x", tc.flags, flags) - } - if iterations != tc.iterations { - t.Errorf("expected %d iterations, got %d", tc.iterations, iterations) - } - } -} - -func TestMetaDevices(t *testing.T) { - d1 := protocol.DeviceID{1} - d2 := protocol.DeviceID{2} - meta := newMetadataTracker(nil, events.NoopLogger) - - meta.addFile(d1, protocol.FileInfo{Sequence: 1}) - meta.addFile(d1, protocol.FileInfo{Sequence: 2, LocalFlags: 1}) - meta.addFile(d2, protocol.FileInfo{Sequence: 1}) - meta.addFile(d2, protocol.FileInfo{Sequence: 2, LocalFlags: 2}) - meta.addFile(protocol.LocalDeviceID, protocol.FileInfo{Sequence: 1}) - - // There are five device/flags combos - if l := len(meta.counts.Counts); l < 5 { - t.Error("expected at least five buckets, not", l) - } - - // There are only two non-local devices - devs := meta.devices() - if l := len(devs); l != 2 { - t.Fatal("expected two devices, not", l) - } - - // Check that we got the two devices we expect - slices.SortFunc(devs, func(a, b protocol.DeviceID) int { - return a.Compare(b) - }) - if devs[0] != d1 { - t.Error("first device should be d1") - } - if devs[1] != d2 { - t.Error("second device should be d2") - } -} - -func TestMetaSequences(t *testing.T) { - d1 := protocol.DeviceID{1} - meta := newMetadataTracker(nil, events.NoopLogger) - - meta.addFile(d1, protocol.FileInfo{Sequence: 1}) - meta.addFile(d1, protocol.FileInfo{Sequence: 2, RawInvalid: true}) - meta.addFile(d1, protocol.FileInfo{Sequence: 3}) - meta.addFile(d1, protocol.FileInfo{Sequence: 4, RawInvalid: true}) - meta.addFile(protocol.LocalDeviceID, protocol.FileInfo{Sequence: 1}) - meta.addFile(protocol.LocalDeviceID, protocol.FileInfo{Sequence: 2}) - meta.addFile(protocol.LocalDeviceID, protocol.FileInfo{Sequence: 3, LocalFlags: 1}) - meta.addFile(protocol.LocalDeviceID, protocol.FileInfo{Sequence: 4, LocalFlags: 2}) - - if seq := meta.Sequence(d1); seq != 4 { - t.Error("sequence of first device should be 4, not", seq) - } - if seq := meta.Sequence(protocol.LocalDeviceID); seq != 4 { - t.Error("sequence of first device should be 4, not", seq) - } -} - -func TestRecalcMeta(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - // Add some files - s1 := newFileSet(t, "test", ldb) - files := []protocol.FileInfo{ - {Name: "a", Size: 1000}, - {Name: "b", Size: 2000}, - } - s1.Update(protocol.LocalDeviceID, files) - - // Verify local/global size - snap := snapshot(t, s1) - ls := snap.LocalSize() - gs := snap.GlobalSize() - snap.Release() - if ls.Bytes != 3000 { - t.Fatalf("Wrong initial local byte count, %d != 3000", ls.Bytes) - } - if gs.Bytes != 3000 { - t.Fatalf("Wrong initial global byte count, %d != 3000", gs.Bytes) - } - - // Reach into the database to make the metadata tracker intentionally - // wrong and out of date - curSeq := s1.meta.Sequence(protocol.LocalDeviceID) - tran, err := ldb.newReadWriteTransaction() - if err != nil { - t.Fatal(err) - } - s1.meta.mut.Lock() - s1.meta.countsPtr(protocol.LocalDeviceID, 0).Sequence = curSeq - 1 // too low - s1.meta.countsPtr(protocol.LocalDeviceID, 0).Bytes = 1234 // wrong - s1.meta.countsPtr(protocol.GlobalDeviceID, 0).Bytes = 1234 // wrong - s1.meta.dirty = true - s1.meta.mut.Unlock() - if err := s1.meta.toDB(tran, []byte("test")); err != nil { - t.Fatal(err) - } - if err := tran.Commit(); err != nil { - t.Fatal(err) - } - - // Verify that our bad data "took" - snap = snapshot(t, s1) - ls = snap.LocalSize() - gs = snap.GlobalSize() - snap.Release() - if ls.Bytes != 1234 { - t.Fatalf("Wrong changed local byte count, %d != 1234", ls.Bytes) - } - if gs.Bytes != 1234 { - t.Fatalf("Wrong changed global byte count, %d != 1234", gs.Bytes) - } - - // Create a new fileset, which will realize the inconsistency and recalculate - s2 := newFileSet(t, "test", ldb) - - // Verify local/global size - snap = snapshot(t, s2) - ls = snap.LocalSize() - gs = snap.GlobalSize() - snap.Release() - if ls.Bytes != 3000 { - t.Fatalf("Wrong fixed local byte count, %d != 3000", ls.Bytes) - } - if gs.Bytes != 3000 { - t.Fatalf("Wrong fixed global byte count, %d != 3000", gs.Bytes) - } -} - -func TestMetaKeyCollisions(t *testing.T) { - if protocol.LocalAllFlags&needFlag != 0 { - t.Error("Collision between need flag and protocol local file flags") - } -} diff --git a/lib/db/namespaced.go b/lib/db/namespaced.go deleted file mode 100644 index ec7f3298c..000000000 --- a/lib/db/namespaced.go +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (C) 2014 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package db - -import ( - "encoding/binary" - "time" - - "github.com/syncthing/syncthing/lib/db/backend" -) - -// NamespacedKV is a simple key-value store using a specific namespace within -// a leveldb. -type NamespacedKV struct { - db backend.Backend - prefix string -} - -// NewNamespacedKV returns a new NamespacedKV that lives in the namespace -// specified by the prefix. -func NewNamespacedKV(db backend.Backend, prefix string) *NamespacedKV { - return &NamespacedKV{ - db: db, - prefix: prefix, - } -} - -// PutInt64 stores a new int64. Any existing value (even if of another type) -// is overwritten. -func (n *NamespacedKV) PutInt64(key string, val int64) error { - var valBs [8]byte - binary.BigEndian.PutUint64(valBs[:], uint64(val)) - return n.db.Put(n.prefixedKey(key), valBs[:]) -} - -// Int64 returns the stored value interpreted as an int64 and a boolean that -// is false if no value was stored at the key. -func (n *NamespacedKV) Int64(key string) (int64, bool, error) { - valBs, err := n.db.Get(n.prefixedKey(key)) - if err != nil { - return 0, false, filterNotFound(err) - } - val := binary.BigEndian.Uint64(valBs) - return int64(val), true, nil -} - -// PutTime stores a new time.Time. Any existing value (even if of another -// type) is overwritten. -func (n *NamespacedKV) PutTime(key string, val time.Time) error { - valBs, _ := val.MarshalBinary() // never returns an error - return n.db.Put(n.prefixedKey(key), valBs) -} - -// Time returns the stored value interpreted as a time.Time and a boolean -// that is false if no value was stored at the key. -func (n NamespacedKV) Time(key string) (time.Time, bool, error) { - var t time.Time - valBs, err := n.db.Get(n.prefixedKey(key)) - if err != nil { - return t, false, filterNotFound(err) - } - err = t.UnmarshalBinary(valBs) - return t, err == nil, err -} - -// PutString stores a new string. Any existing value (even if of another type) -// is overwritten. -func (n *NamespacedKV) PutString(key, val string) error { - return n.db.Put(n.prefixedKey(key), []byte(val)) -} - -// String returns the stored value interpreted as a string and a boolean that -// is false if no value was stored at the key. -func (n NamespacedKV) String(key string) (string, bool, error) { - valBs, err := n.db.Get(n.prefixedKey(key)) - if err != nil { - return "", false, filterNotFound(err) - } - return string(valBs), true, nil -} - -// PutBytes stores a new byte slice. Any existing value (even if of another type) -// is overwritten. -func (n *NamespacedKV) PutBytes(key string, val []byte) error { - return n.db.Put(n.prefixedKey(key), val) -} - -// Bytes returns the stored value as a raw byte slice and a boolean that -// is false if no value was stored at the key. -func (n NamespacedKV) Bytes(key string) ([]byte, bool, error) { - valBs, err := n.db.Get(n.prefixedKey(key)) - if err != nil { - return nil, false, filterNotFound(err) - } - return valBs, true, nil -} - -// PutBool stores a new boolean. Any existing value (even if of another type) -// is overwritten. -func (n *NamespacedKV) PutBool(key string, val bool) error { - if val { - return n.db.Put(n.prefixedKey(key), []byte{0x0}) - } - return n.db.Put(n.prefixedKey(key), []byte{0x1}) -} - -// Bool returns the stored value as a boolean and a boolean that -// is false if no value was stored at the key. -func (n NamespacedKV) Bool(key string) (bool, bool, error) { - valBs, err := n.db.Get(n.prefixedKey(key)) - if err != nil { - return false, false, filterNotFound(err) - } - return valBs[0] == 0x0, true, nil -} - -// Delete deletes the specified key. It is allowed to delete a nonexistent -// key. -func (n NamespacedKV) Delete(key string) error { - return n.db.Delete(n.prefixedKey(key)) -} - -func (n NamespacedKV) prefixedKey(key string) []byte { - return []byte(n.prefix + key) -} - -// Well known namespaces that can be instantiated without knowing the key -// details. - -// NewDeviceStatisticsNamespace creates a KV namespace for device statistics -// for the given device. -func NewDeviceStatisticsNamespace(db backend.Backend, device string) *NamespacedKV { - return NewNamespacedKV(db, string(KeyTypeDeviceStatistic)+device) -} - -// NewFolderStatisticsNamespace creates a KV namespace for folder statistics -// for the given folder. -func NewFolderStatisticsNamespace(db backend.Backend, folder string) *NamespacedKV { - return NewNamespacedKV(db, string(KeyTypeFolderStatistic)+folder) -} - -// NewMiscDataNamespace creates a KV namespace for miscellaneous metadata. -func NewMiscDataNamespace(db backend.Backend) *NamespacedKV { - return NewNamespacedKV(db, string(KeyTypeMiscData)) -} - -func filterNotFound(err error) error { - if backend.IsNotFound(err) { - return nil - } - return err -} diff --git a/lib/db/namespaced_test.go b/lib/db/namespaced_test.go deleted file mode 100644 index bf012d5f3..000000000 --- a/lib/db/namespaced_test.go +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright (C) 2014 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package db - -import ( - "testing" - "time" -) - -func TestNamespacedInt(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - n1 := NewNamespacedKV(ldb, "foo") - n2 := NewNamespacedKV(ldb, "bar") - - // Key is missing to start with - - if v, ok, err := n1.Int64("test"); err != nil { - t.Error("Unexpected error:", err) - } else if v != 0 || ok { - t.Errorf("Incorrect return v %v != 0 || ok %v != false", v, ok) - } - - if err := n1.PutInt64("test", 42); err != nil { - t.Fatal(err) - } - - // It should now exist in n1 - - if v, ok, err := n1.Int64("test"); err != nil { - t.Error("Unexpected error:", err) - } else if v != 42 || !ok { - t.Errorf("Incorrect return v %v != 42 || ok %v != true", v, ok) - } - - // ... but not in n2, which is in a different namespace - - if v, ok, err := n2.Int64("test"); err != nil { - t.Error("Unexpected error:", err) - } else if v != 0 || ok { - t.Errorf("Incorrect return v %v != 0 || ok %v != false", v, ok) - } - - if err := n1.Delete("test"); err != nil { - t.Fatal(err) - } - - // It should no longer exist - - if v, ok, err := n1.Int64("test"); err != nil { - t.Error("Unexpected error:", err) - } else if v != 0 || ok { - t.Errorf("Incorrect return v %v != 0 || ok %v != false", v, ok) - } -} - -func TestNamespacedTime(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - n1 := NewNamespacedKV(ldb, "foo") - - if v, ok, err := n1.Time("test"); err != nil { - t.Error("Unexpected error:", err) - } else if !v.IsZero() || ok { - t.Errorf("Incorrect return v %v != %v || ok %v != false", v, time.Time{}, ok) - } - - now := time.Now() - if err := n1.PutTime("test", now); err != nil { - t.Fatal(err) - } - - if v, ok, err := n1.Time("test"); err != nil { - t.Error("Unexpected error:", err) - } else if !v.Equal(now) || !ok { - t.Errorf("Incorrect return v %v != %v || ok %v != true", v, now, ok) - } -} - -func TestNamespacedString(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - n1 := NewNamespacedKV(ldb, "foo") - - if v, ok, err := n1.String("test"); err != nil { - t.Error("Unexpected error:", err) - } else if v != "" || ok { - t.Errorf("Incorrect return v %q != \"\" || ok %v != false", v, ok) - } - - if err := n1.PutString("test", "yo"); err != nil { - t.Fatal(err) - } - - if v, ok, err := n1.String("test"); err != nil { - t.Error("Unexpected error:", err) - } else if v != "yo" || !ok { - t.Errorf("Incorrect return v %q != \"yo\" || ok %v != true", v, ok) - } -} - -func TestNamespacedReset(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - n1 := NewNamespacedKV(ldb, "foo") - - if err := n1.PutString("test1", "yo1"); err != nil { - t.Fatal(err) - } - if err := n1.PutString("test2", "yo2"); err != nil { - t.Fatal(err) - } - if err := n1.PutString("test3", "yo3"); err != nil { - t.Fatal(err) - } - - if v, ok, err := n1.String("test1"); err != nil { - t.Error("Unexpected error:", err) - } else if v != "yo1" || !ok { - t.Errorf("Incorrect return v %q != \"yo1\" || ok %v != true", v, ok) - } - if v, ok, err := n1.String("test2"); err != nil { - t.Error("Unexpected error:", err) - } else if v != "yo2" || !ok { - t.Errorf("Incorrect return v %q != \"yo2\" || ok %v != true", v, ok) - } - if v, ok, err := n1.String("test3"); err != nil { - t.Error("Unexpected error:", err) - } else if v != "yo3" || !ok { - t.Errorf("Incorrect return v %q != \"yo3\" || ok %v != true", v, ok) - } - - reset(n1) - - if v, ok, err := n1.String("test1"); err != nil { - t.Error("Unexpected error:", err) - } else if v != "" || ok { - t.Errorf("Incorrect return v %q != \"\" || ok %v != false", v, ok) - } - if v, ok, err := n1.String("test2"); err != nil { - t.Error("Unexpected error:", err) - } else if v != "" || ok { - t.Errorf("Incorrect return v %q != \"\" || ok %v != false", v, ok) - } - if v, ok, err := n1.String("test3"); err != nil { - t.Error("Unexpected error:", err) - } else if v != "" || ok { - t.Errorf("Incorrect return v %q != \"\" || ok %v != false", v, ok) - } -} - -// reset removes all entries in this namespace. -func reset(n *NamespacedKV) { - tr, err := n.db.NewWriteTransaction() - if err != nil { - return - } - defer tr.Release() - - it, err := tr.NewPrefixIterator([]byte(n.prefix)) - if err != nil { - return - } - for it.Next() { - _ = tr.Delete(it.Key()) - } - it.Release() - _ = tr.Commit() -} diff --git a/lib/db/schemaupdater.go b/lib/db/schemaupdater.go deleted file mode 100644 index 3a9c4abb2..000000000 --- a/lib/db/schemaupdater.go +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright (C) 2018 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package db - -import ( - "fmt" - - "google.golang.org/protobuf/proto" - - "github.com/syncthing/syncthing/internal/gen/bep" - "github.com/syncthing/syncthing/lib/protocol" -) - -// dbMigrationVersion is for migrations that do not change the schema and thus -// do not put restrictions on downgrades (e.g. for repairs after a bugfix). -const ( - dbVersion = 14 - dbMigrationVersion = 20 - dbMinSyncthingVersion = "v1.9.0" -) - -type migration struct { - schemaVersion int64 - migrationVersion int64 - minSyncthingVersion string - migration func(prevSchema int) error -} - -type databaseDowngradeError struct { - minSyncthingVersion string -} - -func (e *databaseDowngradeError) Error() string { - if e.minSyncthingVersion == "" { - return "newer Syncthing required" - } - return fmt.Sprintf("Syncthing %s required", e.minSyncthingVersion) -} - -// UpdateSchema updates a possibly outdated database to the current schema and -// also does repairs where necessary. -func UpdateSchema(db *Lowlevel) error { - updater := &schemaUpdater{db} - return updater.updateSchema() -} - -type schemaUpdater struct { - *Lowlevel -} - -func (db *schemaUpdater) updateSchema() error { - // Updating the schema can touch any and all parts of the database. Make - // sure we do not run GC concurrently with schema migrations. - db.gcMut.Lock() - defer db.gcMut.Unlock() - - miscDB := NewMiscDataNamespace(db.Lowlevel) - prevVersion, _, err := miscDB.Int64("dbVersion") - if err != nil { - return err - } - - if prevVersion > 0 && prevVersion < 14 { - // This is a database version that is too old to be upgraded directly. - // The user will have to upgrade to an older version first. - return fmt.Errorf("database version %d is too old to be upgraded directly; step via Syncthing v1.27.0 to upgrade", prevVersion) - } - - if prevVersion > dbVersion { - err := &databaseDowngradeError{} - if minSyncthingVersion, ok, dbErr := miscDB.String("dbMinSyncthingVersion"); dbErr != nil { - return dbErr - } else if ok { - err.minSyncthingVersion = minSyncthingVersion - } - return err - } - - prevMigration, _, err := miscDB.Int64("dbMigrationVersion") - if err != nil { - return err - } - // Cover versions before adding `dbMigrationVersion` (== 0) and possible future weirdness. - if prevMigration < prevVersion { - prevMigration = prevVersion - } - - if prevVersion == dbVersion && prevMigration >= dbMigrationVersion { - return nil - } - - migrations := []migration{ - {14, 14, "v1.9.0", db.updateSchemaTo14}, - {14, 16, "v1.9.0", db.checkRepairMigration}, - {14, 17, "v1.9.0", db.migration17}, - {14, 19, "v1.9.0", db.dropAllIndexIDsMigration}, - {14, 20, "v1.9.0", db.dropOutgoingIndexIDsMigration}, - } - - for _, m := range migrations { - if prevMigration < m.migrationVersion { - l.Infof("Running database migration %d...", m.migrationVersion) - if err := m.migration(int(prevVersion)); err != nil { - return fmt.Errorf("failed to do migration %v: %w", m.migrationVersion, err) - } - if err := db.writeVersions(m, miscDB); err != nil { - return fmt.Errorf("failed to write versions after migration %v: %w", m.migrationVersion, err) - } - } - } - - if err := db.writeVersions(migration{ - schemaVersion: dbVersion, - migrationVersion: dbMigrationVersion, - minSyncthingVersion: dbMinSyncthingVersion, - }, miscDB); err != nil { - return fmt.Errorf("failed to write versions after migrations: %w", err) - } - - l.Infoln("Compacting database after migration...") - return db.Compact() -} - -func (*schemaUpdater) writeVersions(m migration, miscDB *NamespacedKV) error { - if err := miscDB.PutInt64("dbVersion", m.schemaVersion); err != nil { - return err - } - if err := miscDB.PutString("dbMinSyncthingVersion", m.minSyncthingVersion); err != nil { - return err - } - if err := miscDB.PutInt64("dbMigrationVersion", m.migrationVersion); err != nil { - return err - } - return nil -} - -func (db *schemaUpdater) updateSchemaTo14(_ int) error { - // Checks for missing blocks and marks those entries as requiring a - // rehash/being invalid. The db is checked/repaired afterwards, i.e. - // no care is taken to get metadata and sequences right. - // If the corresponding files changed on disk compared to the global - // version, this will cause a conflict. - - var key, gk []byte - for _, folderStr := range db.ListFolders() { - folder := []byte(folderStr) - meta := newMetadataTracker(db.keyer, db.evLogger) - meta.counts.Created = 0 // Recalculate metadata afterwards - - t, err := db.newReadWriteTransaction(meta.CommitHook(folder)) - if err != nil { - return err - } - defer t.close() - - key, err = t.keyer.GenerateDeviceFileKey(key, folder, protocol.LocalDeviceID[:], nil) - if err != nil { - return err - } - it, err := t.NewPrefixIterator(key) - if err != nil { - return err - } - defer it.Release() - for it.Next() { - var bepf bep.FileInfo - if err := proto.Unmarshal(it.Value(), &bepf); err != nil { - return err - } - fi := protocol.FileInfoFromDB(&bepf) - if len(fi.Blocks) > 0 || len(fi.BlocksHash) == 0 { - continue - } - key = t.keyer.GenerateBlockListKey(key, fi.BlocksHash) - _, err := t.Get(key) - if err == nil { - continue - } - - fi.SetMustRescan() - if err = t.putFile(it.Key(), fi); err != nil { - return err - } - - gk, err = t.keyer.GenerateGlobalVersionKey(gk, folder, []byte(fi.Name)) - if err != nil { - return err - } - key, err = t.updateGlobal(gk, key, folder, protocol.LocalDeviceID[:], fi, meta) - if err != nil { - return err - } - } - it.Release() - - if err = t.Commit(); err != nil { - return err - } - t.close() - } - - return nil -} - -func (db *schemaUpdater) checkRepairMigration(_ int) error { - for _, folder := range db.ListFolders() { - _, err := db.getMetaAndCheckGCLocked(folder) - if err != nil { - return err - } - } - return nil -} - -// migration17 finds all files that were pulled as invalid from an invalid -// global and make sure they get scanned/pulled again. -func (db *schemaUpdater) migration17(prev int) error { - if prev < 16 { - // Issue was introduced in migration to 16 - return nil - } - t, err := db.newReadOnlyTransaction() - if err != nil { - return err - } - defer t.close() - - for _, folderStr := range db.ListFolders() { - folder := []byte(folderStr) - meta, err := db.loadMetadataTracker(folderStr) - if err != nil { - return err - } - batch := NewFileInfoBatch(func(fs []protocol.FileInfo) error { - return db.updateLocalFiles(folder, fs, meta) - }) - var innerErr error - err = t.withHave(folder, protocol.LocalDeviceID[:], nil, false, func(fi protocol.FileInfo) bool { - if fi.IsInvalid() && fi.FileLocalFlags() == 0 { - fi.SetMustRescan() - fi.Version = protocol.Vector{} - batch.Append(fi) - innerErr = batch.FlushIfFull() - return innerErr == nil - } - return true - }) - if innerErr != nil { - return innerErr - } - if err != nil { - return err - } - if err := batch.Flush(); err != nil { - return err - } - } - return nil -} - -func (db *schemaUpdater) dropAllIndexIDsMigration(_ int) error { - return db.dropIndexIDs() -} - -func (db *schemaUpdater) dropOutgoingIndexIDsMigration(_ int) error { - return db.dropOtherDeviceIndexIDs() -} diff --git a/lib/db/set.go b/lib/db/set.go deleted file mode 100644 index 06e91e14b..000000000 --- a/lib/db/set.go +++ /dev/null @@ -1,553 +0,0 @@ -// Copyright (C) 2014 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -// Package db provides a set type to track local/remote files with newness -// checks. We must do a certain amount of normalization in here. We will get -// fed paths with either native or wire-format separators and encodings -// depending on who calls us. We transform paths to wire-format (NFC and -// slashes) on the way to the database, and transform to native format -// (varying separator and encoding) on the way back out. -package db - -import ( - "bytes" - "fmt" - - "github.com/syncthing/syncthing/internal/gen/dbproto" - "github.com/syncthing/syncthing/lib/db/backend" - "github.com/syncthing/syncthing/lib/fs" - "github.com/syncthing/syncthing/lib/osutil" - "github.com/syncthing/syncthing/lib/protocol" - "github.com/syncthing/syncthing/lib/sync" -) - -type FileSet struct { - folder string - db *Lowlevel - meta *metadataTracker - - updateMutex sync.Mutex // protects database updates and the corresponding metadata changes -} - -// The Iterator is called with either a protocol.FileInfo or a -// FileInfoTruncated (depending on the method) and returns true to -// continue iteration, false to stop. -type Iterator func(f protocol.FileInfo) bool - -func NewFileSet(folder string, db *Lowlevel) (*FileSet, error) { - select { - case <-db.oneFileSetCreated: - default: - close(db.oneFileSetCreated) - } - meta, err := db.loadMetadataTracker(folder) - if err != nil { - db.handleFailure(err) - return nil, err - } - s := &FileSet{ - folder: folder, - db: db, - meta: meta, - updateMutex: sync.NewMutex(), - } - if id := s.IndexID(protocol.LocalDeviceID); id == 0 { - // No index ID set yet. We create one now. - id = protocol.NewIndexID() - err := s.db.setIndexID(protocol.LocalDeviceID[:], []byte(s.folder), id) - if err != nil && !backend.IsClosed(err) { - fatalError(err, fmt.Sprintf("%s Creating new IndexID", s.folder), s.db) - } - } - return s, nil -} - -func (s *FileSet) Drop(device protocol.DeviceID) { - opStr := fmt.Sprintf("%s Drop(%v)", s.folder, device) - l.Debugf(opStr) - - s.updateMutex.Lock() - defer s.updateMutex.Unlock() - - if err := s.db.dropDeviceFolder(device[:], []byte(s.folder), s.meta); backend.IsClosed(err) { - return - } else if err != nil { - fatalError(err, opStr, s.db) - } - - if device == protocol.LocalDeviceID { - s.meta.resetCounts(device) - // We deliberately do not reset the sequence number here. Dropping - // all files for the local device ID only happens in testing - which - // expects the sequence to be retained, like an old Replace() of all - // files would do. However, if we ever did it "in production" we - // would anyway want to retain the sequence for delta indexes to be - // happy. - } else { - // Here, on the other hand, we want to make sure that any file - // announced from the remote is newer than our current sequence - // number. - s.meta.resetAll(device) - } - - t, err := s.db.newReadWriteTransaction() - if backend.IsClosed(err) { - return - } else if err != nil { - fatalError(err, opStr, s.db) - } - defer t.close() - - if err := s.meta.toDB(t, []byte(s.folder)); backend.IsClosed(err) { - return - } else if err != nil { - fatalError(err, opStr, s.db) - } - if err := t.Commit(); backend.IsClosed(err) { - return - } else if err != nil { - fatalError(err, opStr, s.db) - } -} - -func (s *FileSet) Update(device protocol.DeviceID, fs []protocol.FileInfo) { - opStr := fmt.Sprintf("%s Update(%v, [%d])", s.folder, device, len(fs)) - l.Debugf(opStr) - - // do not modify fs in place, it is still used in outer scope - fs = append([]protocol.FileInfo(nil), fs...) - - // If one file info is present multiple times, only keep the last. - // Updating the same file multiple times is problematic, because the - // previous updates won't yet be represented in the db when we update it - // again. Additionally even if that problem was taken care of, it would - // be pointless because we remove the previously added file info again - // right away. - fs = normalizeFilenamesAndDropDuplicates(fs) - - s.updateMutex.Lock() - defer s.updateMutex.Unlock() - - if device == protocol.LocalDeviceID { - // For the local device we have a bunch of metadata to track. - if err := s.db.updateLocalFiles([]byte(s.folder), fs, s.meta); err != nil && !backend.IsClosed(err) { - fatalError(err, opStr, s.db) - } - return - } - // Easy case, just update the files and we're done. - if err := s.db.updateRemoteFiles([]byte(s.folder), device[:], fs, s.meta); err != nil && !backend.IsClosed(err) { - fatalError(err, opStr, s.db) - } -} - -func (s *FileSet) RemoveLocalItems(items []string) { - opStr := fmt.Sprintf("%s RemoveLocalItems([%d])", s.folder, len(items)) - l.Debugf(opStr) - - s.updateMutex.Lock() - defer s.updateMutex.Unlock() - - for i := range items { - items[i] = osutil.NormalizedFilename(items[i]) - } - - if err := s.db.removeLocalFiles([]byte(s.folder), items, s.meta); err != nil && !backend.IsClosed(err) { - fatalError(err, opStr, s.db) - } -} - -type Snapshot struct { - folder string - t readOnlyTransaction - meta *countsMap - fatalError func(error, string) -} - -func (s *FileSet) Snapshot() (*Snapshot, error) { - opStr := fmt.Sprintf("%s Snapshot()", s.folder) - l.Debugf(opStr) - - s.updateMutex.Lock() - defer s.updateMutex.Unlock() - - t, err := s.db.newReadOnlyTransaction() - if err != nil { - s.db.handleFailure(err) - return nil, err - } - return &Snapshot{ - folder: s.folder, - t: t, - meta: s.meta.Snapshot(), - fatalError: func(err error, opStr string) { - fatalError(err, opStr, s.db) - }, - }, nil -} - -func (s *Snapshot) Release() { - s.t.close() -} - -func (s *Snapshot) WithNeed(device protocol.DeviceID, fn Iterator) { - opStr := fmt.Sprintf("%s WithNeed(%v)", s.folder, device) - l.Debugf(opStr) - if err := s.t.withNeed([]byte(s.folder), device[:], false, nativeFileIterator(fn)); err != nil && !backend.IsClosed(err) { - s.fatalError(err, opStr) - } -} - -func (s *Snapshot) WithNeedTruncated(device protocol.DeviceID, fn Iterator) { - opStr := fmt.Sprintf("%s WithNeedTruncated(%v)", s.folder, device) - l.Debugf(opStr) - if err := s.t.withNeed([]byte(s.folder), device[:], true, nativeFileIterator(fn)); err != nil && !backend.IsClosed(err) { - s.fatalError(err, opStr) - } -} - -func (s *Snapshot) WithHave(device protocol.DeviceID, fn Iterator) { - opStr := fmt.Sprintf("%s WithHave(%v)", s.folder, device) - l.Debugf(opStr) - if err := s.t.withHave([]byte(s.folder), device[:], nil, false, nativeFileIterator(fn)); err != nil && !backend.IsClosed(err) { - s.fatalError(err, opStr) - } -} - -func (s *Snapshot) WithHaveTruncated(device protocol.DeviceID, fn Iterator) { - opStr := fmt.Sprintf("%s WithHaveTruncated(%v)", s.folder, device) - l.Debugf(opStr) - if err := s.t.withHave([]byte(s.folder), device[:], nil, true, nativeFileIterator(fn)); err != nil && !backend.IsClosed(err) { - s.fatalError(err, opStr) - } -} - -func (s *Snapshot) WithHaveSequence(startSeq int64, fn Iterator) { - opStr := fmt.Sprintf("%s WithHaveSequence(%v)", s.folder, startSeq) - l.Debugf(opStr) - if err := s.t.withHaveSequence([]byte(s.folder), startSeq, nativeFileIterator(fn)); err != nil && !backend.IsClosed(err) { - s.fatalError(err, opStr) - } -} - -// Except for an item with a path equal to prefix, only children of prefix are iterated. -// E.g. for prefix "dir", "dir/file" is iterated, but "dir.file" is not. -func (s *Snapshot) WithPrefixedHaveTruncated(device protocol.DeviceID, prefix string, fn Iterator) { - opStr := fmt.Sprintf(`%s WithPrefixedHaveTruncated(%v, "%v")`, s.folder, device, prefix) - l.Debugf(opStr) - if err := s.t.withHave([]byte(s.folder), device[:], []byte(osutil.NormalizedFilename(prefix)), true, nativeFileIterator(fn)); err != nil && !backend.IsClosed(err) { - s.fatalError(err, opStr) - } -} - -func (s *Snapshot) WithGlobal(fn Iterator) { - opStr := fmt.Sprintf("%s WithGlobal()", s.folder) - l.Debugf(opStr) - if err := s.t.withGlobal([]byte(s.folder), nil, false, nativeFileIterator(fn)); err != nil && !backend.IsClosed(err) { - s.fatalError(err, opStr) - } -} - -func (s *Snapshot) WithGlobalTruncated(fn Iterator) { - opStr := fmt.Sprintf("%s WithGlobalTruncated()", s.folder) - l.Debugf(opStr) - if err := s.t.withGlobal([]byte(s.folder), nil, true, nativeFileIterator(fn)); err != nil && !backend.IsClosed(err) { - s.fatalError(err, opStr) - } -} - -// Except for an item with a path equal to prefix, only children of prefix are iterated. -// E.g. for prefix "dir", "dir/file" is iterated, but "dir.file" is not. -func (s *Snapshot) WithPrefixedGlobalTruncated(prefix string, fn Iterator) { - opStr := fmt.Sprintf(`%s WithPrefixedGlobalTruncated("%v")`, s.folder, prefix) - l.Debugf(opStr) - if err := s.t.withGlobal([]byte(s.folder), []byte(osutil.NormalizedFilename(prefix)), true, nativeFileIterator(fn)); err != nil && !backend.IsClosed(err) { - s.fatalError(err, opStr) - } -} - -func (s *Snapshot) Get(device protocol.DeviceID, file string) (protocol.FileInfo, bool) { - opStr := fmt.Sprintf("%s Get(%v)", s.folder, file) - l.Debugf(opStr) - f, ok, err := s.t.getFile([]byte(s.folder), device[:], []byte(osutil.NormalizedFilename(file))) - if backend.IsClosed(err) { - return protocol.FileInfo{}, false - } else if err != nil { - s.fatalError(err, opStr) - } - f.Name = osutil.NativeFilename(f.Name) - return f, ok -} - -func (s *Snapshot) GetGlobal(file string) (protocol.FileInfo, bool) { - opStr := fmt.Sprintf("%s GetGlobal(%v)", s.folder, file) - l.Debugf(opStr) - _, fi, ok, err := s.t.getGlobal(nil, []byte(s.folder), []byte(osutil.NormalizedFilename(file)), false) - if backend.IsClosed(err) { - return protocol.FileInfo{}, false - } else if err != nil { - s.fatalError(err, opStr) - } - if !ok { - return protocol.FileInfo{}, false - } - fi.Name = osutil.NativeFilename(fi.Name) - return fi, true -} - -func (s *Snapshot) GetGlobalTruncated(file string) (protocol.FileInfo, bool) { - opStr := fmt.Sprintf("%s GetGlobalTruncated(%v)", s.folder, file) - l.Debugf(opStr) - _, fi, ok, err := s.t.getGlobal(nil, []byte(s.folder), []byte(osutil.NormalizedFilename(file)), true) - if backend.IsClosed(err) { - return protocol.FileInfo{}, false - } else if err != nil { - s.fatalError(err, opStr) - } - if !ok { - return protocol.FileInfo{}, false - } - fi.Name = osutil.NativeFilename(fi.Name) - return fi, true -} - -func (s *Snapshot) Availability(file string) []protocol.DeviceID { - opStr := fmt.Sprintf("%s Availability(%v)", s.folder, file) - l.Debugf(opStr) - av, err := s.t.availability([]byte(s.folder), []byte(osutil.NormalizedFilename(file))) - if backend.IsClosed(err) { - return nil - } else if err != nil { - s.fatalError(err, opStr) - } - return av -} - -func (s *Snapshot) DebugGlobalVersions(file string) *DebugVersionList { - opStr := fmt.Sprintf("%s DebugGlobalVersions(%v)", s.folder, file) - l.Debugf(opStr) - vl, err := s.t.getGlobalVersions(nil, []byte(s.folder), []byte(osutil.NormalizedFilename(file))) - if backend.IsClosed(err) || backend.IsNotFound(err) { - return nil - } else if err != nil { - s.fatalError(err, opStr) - } - return &DebugVersionList{vl} -} - -func (s *Snapshot) Sequence(device protocol.DeviceID) int64 { - return s.meta.Counts(device, 0).Sequence -} - -// RemoteSequences returns a map of the sequence numbers seen for each -// remote device sharing this folder. -func (s *Snapshot) RemoteSequences() map[protocol.DeviceID]int64 { - res := make(map[protocol.DeviceID]int64) - for _, device := range s.meta.devices() { - switch device { - case protocol.EmptyDeviceID, protocol.LocalDeviceID, protocol.GlobalDeviceID: - continue - default: - if seq := s.Sequence(device); seq > 0 { - res[device] = seq - } - } - } - - return res -} - -func (s *Snapshot) LocalSize() Counts { - local := s.meta.Counts(protocol.LocalDeviceID, 0) - return local.Add(s.ReceiveOnlyChangedSize()) -} - -func (s *Snapshot) ReceiveOnlyChangedSize() Counts { - return s.meta.Counts(protocol.LocalDeviceID, protocol.FlagLocalReceiveOnly) -} - -func (s *Snapshot) GlobalSize() Counts { - return s.meta.Counts(protocol.GlobalDeviceID, 0) -} - -func (s *Snapshot) NeedSize(device protocol.DeviceID) Counts { - return s.meta.Counts(device, needFlag) -} - -func (s *Snapshot) WithBlocksHash(hash []byte, fn Iterator) { - opStr := fmt.Sprintf(`%s WithBlocksHash("%x")`, s.folder, hash) - l.Debugf(opStr) - if err := s.t.withBlocksHash([]byte(s.folder), hash, nativeFileIterator(fn)); err != nil && !backend.IsClosed(err) { - s.fatalError(err, opStr) - } -} - -func (s *FileSet) Sequence(device protocol.DeviceID) int64 { - return s.meta.Sequence(device) -} - -func (s *FileSet) IndexID(device protocol.DeviceID) protocol.IndexID { - opStr := fmt.Sprintf("%s IndexID(%v)", s.folder, device) - l.Debugf(opStr) - id, err := s.db.getIndexID(device[:], []byte(s.folder)) - if backend.IsClosed(err) { - return 0 - } else if err != nil { - fatalError(err, opStr, s.db) - } - return id -} - -func (s *FileSet) SetIndexID(device protocol.DeviceID, id protocol.IndexID) { - if device == protocol.LocalDeviceID { - panic("do not explicitly set index ID for local device") - } - opStr := fmt.Sprintf("%s SetIndexID(%v, %v)", s.folder, device, id) - l.Debugf(opStr) - if err := s.db.setIndexID(device[:], []byte(s.folder), id); err != nil && !backend.IsClosed(err) { - fatalError(err, opStr, s.db) - } -} - -func (s *FileSet) MtimeOption() fs.Option { - opStr := fmt.Sprintf("%s MtimeOption()", s.folder) - l.Debugf(opStr) - prefix, err := s.db.keyer.GenerateMtimesKey(nil, []byte(s.folder)) - if backend.IsClosed(err) { - return nil - } else if err != nil { - fatalError(err, opStr, s.db) - } - kv := NewNamespacedKV(s.db, string(prefix)) - return fs.NewMtimeOption(kv) -} - -func (s *FileSet) ListDevices() []protocol.DeviceID { - return s.meta.devices() -} - -func (s *FileSet) RepairSequence() (int, error) { - s.updateAndGCMutexLock() // Ensures consistent locking order - defer s.updateMutex.Unlock() - defer s.db.gcMut.RUnlock() - return s.db.repairSequenceGCLocked(s.folder, s.meta) -} - -func (s *FileSet) updateAndGCMutexLock() { - s.updateMutex.Lock() - s.db.gcMut.RLock() -} - -// DropFolder clears out all information related to the given folder from the -// database. -func DropFolder(db *Lowlevel, folder string) { - opStr := fmt.Sprintf("DropFolder(%v)", folder) - l.Debugf(opStr) - droppers := []func([]byte) error{ - db.dropFolder, - db.dropMtimes, - db.dropFolderMeta, - db.dropFolderIndexIDs, - db.folderIdx.Delete, - } - for _, drop := range droppers { - if err := drop([]byte(folder)); backend.IsClosed(err) { - return - } else if err != nil { - fatalError(err, opStr, db) - } - } -} - -// DropDeltaIndexIDs removes all delta index IDs from the database. -// This will cause a full index transmission on the next connection. -// Must be called before using FileSets, i.e. before NewFileSet is called for -// the first time. -func DropDeltaIndexIDs(db *Lowlevel) { - select { - case <-db.oneFileSetCreated: - panic("DropDeltaIndexIDs must not be called after NewFileSet for the same Lowlevel") - default: - } - opStr := "DropDeltaIndexIDs" - l.Debugf(opStr) - err := db.dropIndexIDs() - if backend.IsClosed(err) { - return - } else if err != nil { - fatalError(err, opStr, db) - } -} - -func normalizeFilenamesAndDropDuplicates(fs []protocol.FileInfo) []protocol.FileInfo { - positions := make(map[string]int, len(fs)) - for i, f := range fs { - norm := osutil.NormalizedFilename(f.Name) - if pos, ok := positions[norm]; ok { - fs[pos] = protocol.FileInfo{} - } - positions[norm] = i - fs[i].Name = norm - } - for i := 0; i < len(fs); { - if fs[i].Name == "" { - fs = append(fs[:i], fs[i+1:]...) - continue - } - i++ - } - return fs -} - -func nativeFileIterator(fn Iterator) Iterator { - return func(fi protocol.FileInfo) bool { - fi.Name = osutil.NativeFilename(fi.Name) - return fn(fi) - } -} - -func fatalError(err error, opStr string, db *Lowlevel) { - db.checkErrorForRepair(err) - l.Warnf("Fatal error: %v: %v", opStr, err) - panic(ldbPathRe.ReplaceAllString(err.Error(), "$1 x: ")) -} - -// DebugFileVersion is the database-internal representation of a file -// version, with a nicer string representation, used only by API debug -// methods. -type DebugVersionList struct { - *dbproto.VersionList -} - -func (vl DebugVersionList) String() string { - var b bytes.Buffer - var id protocol.DeviceID - b.WriteString("[") - for i, v := range vl.Versions { - if i > 0 { - b.WriteString(", ") - } - fmt.Fprintf(&b, "{Version:%v, Deleted:%v, Devices:[", protocol.VectorFromWire(v.Version), v.Deleted) - for j, dev := range v.Devices { - if j > 0 { - b.WriteString(", ") - } - copy(id[:], dev) - fmt.Fprint(&b, id.Short()) - } - b.WriteString("], Invalid:[") - for j, dev := range v.InvalidDevices { - if j > 0 { - b.WriteString(", ") - } - copy(id[:], dev) - fmt.Fprint(&b, id.Short()) - } - fmt.Fprint(&b, "]}") - } - b.WriteString("]") - return b.String() -} diff --git a/lib/db/set_test.go b/lib/db/set_test.go deleted file mode 100644 index 36cef101b..000000000 --- a/lib/db/set_test.go +++ /dev/null @@ -1,1894 +0,0 @@ -// Copyright (C) 2014 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package db_test - -import ( - "bytes" - "fmt" - "os" - "path/filepath" - "slices" - "strings" - "testing" - "time" - - "github.com/d4l3k/messagediff" - - "github.com/syncthing/syncthing/lib/db" - "github.com/syncthing/syncthing/lib/db/backend" - "github.com/syncthing/syncthing/lib/events" - "github.com/syncthing/syncthing/lib/protocol" -) - -var remoteDevice0, remoteDevice1 protocol.DeviceID - -func init() { - remoteDevice0, _ = protocol.DeviceIDFromString("AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR") - remoteDevice1, _ = protocol.DeviceIDFromString("I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU") -} - -const myID = 1 - -func genBlocks(n int) []protocol.BlockInfo { - b := make([]protocol.BlockInfo, n) - for i := range b { - h := make([]byte, 32) - for j := range h { - h[j] = byte(i + j) - } - b[i].Size = i - b[i].Hash = h - } - return b -} - -func globalList(t testing.TB, s *db.FileSet) []protocol.FileInfo { - var fs []protocol.FileInfo - snap := snapshot(t, s) - defer snap.Release() - snap.WithGlobal(func(fi protocol.FileInfo) bool { - fs = append(fs, fi) - return true - }) - return fs -} - -func globalListPrefixed(t testing.TB, s *db.FileSet, prefix string) []protocol.FileInfo { - var fs []protocol.FileInfo - snap := snapshot(t, s) - defer snap.Release() - snap.WithPrefixedGlobalTruncated(prefix, func(fi protocol.FileInfo) bool { - fs = append(fs, fi) - return true - }) - return fs -} - -func haveList(t testing.TB, s *db.FileSet, n protocol.DeviceID) []protocol.FileInfo { - var fs []protocol.FileInfo - snap := snapshot(t, s) - defer snap.Release() - snap.WithHave(n, func(fi protocol.FileInfo) bool { - fs = append(fs, fi) - return true - }) - return fs -} - -func haveListPrefixed(t testing.TB, s *db.FileSet, n protocol.DeviceID, prefix string) []protocol.FileInfo { - var fs []protocol.FileInfo - snap := snapshot(t, s) - defer snap.Release() - snap.WithPrefixedHaveTruncated(n, prefix, func(fi protocol.FileInfo) bool { - fs = append(fs, fi) - return true - }) - return fs -} - -func needList(t testing.TB, s *db.FileSet, n protocol.DeviceID) []protocol.FileInfo { - var fs []protocol.FileInfo - snap := snapshot(t, s) - defer snap.Release() - snap.WithNeed(n, func(fi protocol.FileInfo) bool { - fs = append(fs, fi) - return true - }) - return fs -} - -type fileList []protocol.FileInfo - -func compareByName(a, b protocol.FileInfo) int { - return strings.Compare(a.Name, b.Name) -} - -func (l fileList) String() string { - var b bytes.Buffer - b.WriteString("[]protocol.FileList{\n") - for _, f := range l { - fmt.Fprintf(&b, " %q: #%v, %d bytes, %d blocks, perms=%o\n", f.Name, f.Version, f.Size, len(f.Blocks), f.Permissions) - } - b.WriteString("}") - return b.String() -} - -func setSequence(seq int64, files fileList) int64 { - for i := range files { - seq++ - files[i].Sequence = seq - } - return seq -} - -func setBlocksHash(files fileList) { - for i, f := range files { - files[i].BlocksHash = protocol.BlocksHash(f.Blocks) - } -} - -func TestGlobalSet(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - m := newFileSet(t, "test", ldb) - - local0 := fileList{ - protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)}, - protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(2)}, - protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(3)}, - protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(4)}, - protocol.FileInfo{Name: "z", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(8)}, - } - localSeq := setSequence(0, local0) - setBlocksHash(local0) - local1 := fileList{ - protocol.FileInfo{Name: "a", Sequence: 6, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)}, - protocol.FileInfo{Name: "b", Sequence: 7, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(2)}, - protocol.FileInfo{Name: "c", Sequence: 8, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(3)}, - protocol.FileInfo{Name: "d", Sequence: 9, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(4)}, - protocol.FileInfo{Name: "z", Sequence: 10, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Deleted: true}, - } - setSequence(localSeq, local1) - setBlocksHash(local1) - localTot := fileList{ - local1[0], - local1[1], - local1[2], - local1[3], - protocol.FileInfo{Name: "z", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Deleted: true}, - } - - remote0 := fileList{ - protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)}, - protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(2)}, - protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(5)}, - } - remoteSeq := setSequence(0, remote0) - setBlocksHash(remote0) - remote1 := fileList{ - protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(6)}, - protocol.FileInfo{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(7)}, - } - setSequence(remoteSeq, remote1) - setBlocksHash(remote1) - remoteTot := fileList{ - remote0[0], - remote1[0], - remote0[2], - remote1[1], - } - - expectedGlobal := fileList{ - remote0[0], // a - remote1[0], // b - remote0[2], // c - localTot[3], // d - remote1[1], // e - localTot[4], // z - } - - expectedLocalNeed := fileList{ - remote1[0], - remote0[2], - remote1[1], - } - - expectedRemoteNeed := fileList{ - local0[3], - } - - replace(m, protocol.LocalDeviceID, local0) - replace(m, protocol.LocalDeviceID, local1) - replace(m, remoteDevice0, remote0) - m.Update(remoteDevice0, remote1) - - check := func() { - t.Helper() - - g := fileList(globalList(t, m)) - slices.SortFunc(g, compareByName) - - if fmt.Sprint(g) != fmt.Sprint(expectedGlobal) { - t.Errorf("Global incorrect;\n A: %v !=\n E: %v", g, expectedGlobal) - } - - var globalFiles, globalDirectories, globalDeleted int - var globalBytes int64 - for _, f := range g { - if f.IsInvalid() { - continue - } - switch { - case f.IsDeleted(): - globalDeleted++ - case f.IsDirectory(): - globalDirectories++ - default: - globalFiles++ - } - globalBytes += f.FileSize() - } - gs := globalSize(t, m) - if gs.Files != globalFiles { - t.Errorf("Incorrect GlobalSize files; %d != %d", gs.Files, globalFiles) - } - if gs.Directories != globalDirectories { - t.Errorf("Incorrect GlobalSize directories; %d != %d", gs.Directories, globalDirectories) - } - if gs.Deleted != globalDeleted { - t.Errorf("Incorrect GlobalSize deleted; %d != %d", gs.Deleted, globalDeleted) - } - if gs.Bytes != globalBytes { - t.Errorf("Incorrect GlobalSize bytes; %d != %d", gs.Bytes, globalBytes) - } - - h := fileList(haveList(t, m, protocol.LocalDeviceID)) - slices.SortFunc(h, compareByName) - - if fmt.Sprint(h) != fmt.Sprint(localTot) { - t.Errorf("Have incorrect (local);\n A: %v !=\n E: %v", h, localTot) - } - - var haveFiles, haveDirectories, haveDeleted int - var haveBytes int64 - for _, f := range h { - if f.IsInvalid() { - continue - } - switch { - case f.IsDeleted(): - haveDeleted++ - case f.IsDirectory(): - haveDirectories++ - default: - haveFiles++ - } - haveBytes += f.FileSize() - } - ls := localSize(t, m) - if ls.Files != haveFiles { - t.Errorf("Incorrect LocalSize files; %d != %d", ls.Files, haveFiles) - } - if ls.Directories != haveDirectories { - t.Errorf("Incorrect LocalSize directories; %d != %d", ls.Directories, haveDirectories) - } - if ls.Deleted != haveDeleted { - t.Errorf("Incorrect LocalSize deleted; %d != %d", ls.Deleted, haveDeleted) - } - if ls.Bytes != haveBytes { - t.Errorf("Incorrect LocalSize bytes; %d != %d", ls.Bytes, haveBytes) - } - - h = fileList(haveList(t, m, remoteDevice0)) - slices.SortFunc(h, compareByName) - - if fmt.Sprint(h) != fmt.Sprint(remoteTot) { - t.Errorf("Have incorrect (remote);\n A: %v !=\n E: %v", h, remoteTot) - } - - n := fileList(needList(t, m, protocol.LocalDeviceID)) - slices.SortFunc(n, compareByName) - - if fmt.Sprint(n) != fmt.Sprint(expectedLocalNeed) { - t.Errorf("Need incorrect (local);\n A: %v !=\n E: %v", n, expectedLocalNeed) - } - - checkNeed(t, m, protocol.LocalDeviceID, expectedLocalNeed) - - n = fileList(needList(t, m, remoteDevice0)) - slices.SortFunc(n, compareByName) - - if fmt.Sprint(n) != fmt.Sprint(expectedRemoteNeed) { - t.Errorf("Need incorrect (remote);\n A: %v !=\n E: %v", n, expectedRemoteNeed) - } - - checkNeed(t, m, remoteDevice0, expectedRemoteNeed) - - snap := snapshot(t, m) - defer snap.Release() - f, ok := snap.Get(protocol.LocalDeviceID, "b") - if !ok { - t.Error("Unexpectedly not OK") - } - if fmt.Sprint(f) != fmt.Sprint(localTot[1]) { - t.Errorf("Get incorrect;\n A: %v !=\n E: %v", f, localTot[1]) - } - - f, ok = snap.Get(remoteDevice0, "b") - if !ok { - t.Error("Unexpectedly not OK") - } - if fmt.Sprint(f) != fmt.Sprint(remote1[0]) { - t.Errorf("Get incorrect (remote);\n A: %v !=\n E: %v", f, remote1[0]) - } - - f, ok = snap.GetGlobal("b") - if !ok { - t.Error("Unexpectedly not OK") - } - if fmt.Sprint(f) != fmt.Sprint(expectedGlobal[1]) { - t.Errorf("GetGlobal incorrect;\n A: %v !=\n E: %v", f, remote1[0]) - } - - f, ok = snap.Get(protocol.LocalDeviceID, "zz") - if ok { - t.Error("Unexpectedly OK") - } - if f.Name != "" { - t.Errorf("Get incorrect (local);\n A: %v !=\n E: %v", f, protocol.FileInfo{}) - } - - f, ok = snap.GetGlobal("zz") - if ok { - t.Error("Unexpectedly OK") - } - if f.Name != "" { - t.Errorf("GetGlobal incorrect;\n A: %v !=\n E: %v", f, protocol.FileInfo{}) - } - } - - check() - - snap := snapshot(t, m) - - av := []protocol.DeviceID{protocol.LocalDeviceID, remoteDevice0} - a := snap.Availability("a") - if !(len(a) == 2 && (a[0] == av[0] && a[1] == av[1] || a[0] == av[1] && a[1] == av[0])) { - t.Errorf("Availability incorrect;\n A: %v !=\n E: %v", a, av) - } - a = snap.Availability("b") - if len(a) != 1 || a[0] != remoteDevice0 { - t.Errorf("Availability incorrect;\n A: %v !=\n E: %v", a, remoteDevice0) - } - a = snap.Availability("d") - if len(a) != 1 || a[0] != protocol.LocalDeviceID { - t.Errorf("Availability incorrect;\n A: %v !=\n E: %v", a, protocol.LocalDeviceID) - } - - snap.Release() - - // Now bring another remote into play - - secRemote := fileList{ - local1[0], // a - remote1[0], // b - local1[3], // d - remote1[1], // e - local1[4], // z - } - secRemote[0].Version = secRemote[0].Version.Update(remoteDevice1.Short()) - secRemote[1].Version = secRemote[1].Version.Update(remoteDevice1.Short()) - secRemote[4].Version = secRemote[4].Version.Update(remoteDevice1.Short()) - secRemote[4].Deleted = false - secRemote[4].Blocks = genBlocks(1) - setSequence(0, secRemote) - - expectedGlobal = fileList{ - secRemote[0], // a - secRemote[1], // b - remote0[2], // c - localTot[3], // d - secRemote[3], // e - secRemote[4], // z - } - - expectedLocalNeed = fileList{ - secRemote[0], // a - secRemote[1], // b - remote0[2], // c - secRemote[3], // e - secRemote[4], // z - } - - expectedRemoteNeed = fileList{ - secRemote[0], // a - secRemote[1], // b - local0[3], // d - secRemote[4], // z - } - - expectedSecRemoteNeed := fileList{ - remote0[2], // c - } - - m.Update(remoteDevice1, secRemote) - - check() - - h := fileList(haveList(t, m, remoteDevice1)) - slices.SortFunc(h, compareByName) - - if fmt.Sprint(h) != fmt.Sprint(secRemote) { - t.Errorf("Have incorrect (secRemote);\n A: %v !=\n E: %v", h, secRemote) - } - - n := fileList(needList(t, m, remoteDevice1)) - slices.SortFunc(n, compareByName) - - if fmt.Sprint(n) != fmt.Sprint(expectedSecRemoteNeed) { - t.Errorf("Need incorrect (secRemote);\n A: %v !=\n E: %v", n, expectedSecRemoteNeed) - } - - checkNeed(t, m, remoteDevice1, expectedSecRemoteNeed) -} - -func TestNeedWithInvalid(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - s := newFileSet(t, "test", ldb) - - localHave := fileList{ - protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)}, - } - remote0Have := fileList{ - protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)}, - protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), RawInvalid: true}, - protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)}, - } - remote1Have := fileList{ - protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(7)}, - protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(5), RawInvalid: true}, - protocol.FileInfo{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), RawInvalid: true}, - } - - expectedNeed := fileList{ - remote0Have[0], - remote1Have[0], - remote0Have[2], - } - - replace(s, protocol.LocalDeviceID, localHave) - replace(s, remoteDevice0, remote0Have) - replace(s, remoteDevice1, remote1Have) - - need := fileList(needList(t, s, protocol.LocalDeviceID)) - slices.SortFunc(need, compareByName) - - if fmt.Sprint(need) != fmt.Sprint(expectedNeed) { - t.Errorf("Need incorrect;\n A: %v !=\n E: %v", need, expectedNeed) - } - - checkNeed(t, s, protocol.LocalDeviceID, expectedNeed) -} - -func TestUpdateToInvalid(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - folder := "test" - s := newFileSet(t, folder, ldb) - f := db.NewBlockFinder(ldb) - - localHave := fileList{ - protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1), Size: 1}, - protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2), Size: 1}, - protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), LocalFlags: protocol.FlagLocalIgnored, Size: 1}, - protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7), Size: 1}, - protocol.FileInfo{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, LocalFlags: protocol.FlagLocalIgnored, Size: 1}, - } - - replace(s, protocol.LocalDeviceID, localHave) - - have := fileList(haveList(t, s, protocol.LocalDeviceID)) - slices.SortFunc(have, compareByName) - - if fmt.Sprint(have) != fmt.Sprint(localHave) { - t.Errorf("Have incorrect before invalidation;\n A: %v !=\n E: %v", have, localHave) - } - - oldBlockHash := localHave[1].Blocks[0].Hash - - localHave[1].LocalFlags = protocol.FlagLocalIgnored - localHave[1].Blocks = nil - - localHave[4].LocalFlags = 0 - localHave[4].Blocks = genBlocks(3) - - s.Update(protocol.LocalDeviceID, append(fileList{}, localHave[1], localHave[4])) - - have = haveList(t, s, protocol.LocalDeviceID) - slices.SortFunc(have, compareByName) - - if fmt.Sprint(have) != fmt.Sprint(localHave) { - t.Errorf("Have incorrect after invalidation;\n A: %v !=\n E: %v", have, localHave) - } - - f.Iterate([]string{folder}, oldBlockHash, func(folder, file string, index int32) bool { - if file == localHave[1].Name { - t.Errorf("Found unexpected block in blockmap for invalidated file") - return true - } - return false - }) - - if !f.Iterate([]string{folder}, localHave[4].Blocks[0].Hash, func(folder, file string, index int32) bool { - return file == localHave[4].Name - }) { - t.Errorf("First block of un-invalidated file is missing from blockmap") - } -} - -func TestInvalidAvailability(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - s := newFileSet(t, "test", ldb) - - remote0Have := fileList{ - protocol.FileInfo{Name: "both", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)}, - protocol.FileInfo{Name: "r1only", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), RawInvalid: true}, - protocol.FileInfo{Name: "r0only", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)}, - protocol.FileInfo{Name: "none", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), RawInvalid: true}, - } - remote1Have := fileList{ - protocol.FileInfo{Name: "both", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)}, - protocol.FileInfo{Name: "r1only", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(7)}, - protocol.FileInfo{Name: "r0only", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(5), RawInvalid: true}, - protocol.FileInfo{Name: "none", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), RawInvalid: true}, - } - - replace(s, remoteDevice0, remote0Have) - replace(s, remoteDevice1, remote1Have) - - snap := snapshot(t, s) - defer snap.Release() - - if av := snap.Availability("both"); len(av) != 2 { - t.Error("Incorrect availability for 'both':", av) - } - - if av := snap.Availability("r0only"); len(av) != 1 || av[0] != remoteDevice0 { - t.Error("Incorrect availability for 'r0only':", av) - } - - if av := snap.Availability("r1only"); len(av) != 1 || av[0] != remoteDevice1 { - t.Error("Incorrect availability for 'r1only':", av) - } - - if av := snap.Availability("none"); len(av) != 0 { - t.Error("Incorrect availability for 'none':", av) - } -} - -func TestGlobalReset(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - m := newFileSet(t, "test", ldb) - - local := []protocol.FileInfo{ - {Name: "a", Sequence: 1, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - {Name: "b", Sequence: 2, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - {Name: "c", Sequence: 3, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - {Name: "d", Sequence: 4, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - } - - remote := []protocol.FileInfo{ - {Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - {Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}}, - {Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}}, - {Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - } - - replace(m, protocol.LocalDeviceID, local) - g := globalList(t, m) - slices.SortFunc(g, compareByName) - - if diff, equal := messagediff.PrettyDiff(local, g); !equal { - t.Errorf("Global incorrect;\nglobal: %v\n!=\nlocal: %v\ndiff:\n%s", g, local, diff) - } - - replace(m, remoteDevice0, remote) - replace(m, remoteDevice0, nil) - - g = globalList(t, m) - slices.SortFunc(g, compareByName) - - if diff, equal := messagediff.PrettyDiff(local, g); !equal { - t.Errorf("Global incorrect;\nglobal: %v\n!=\nlocal: %v\ndiff:\n%s", g, local, diff) - } -} - -func TestNeed(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - m := newFileSet(t, "test", ldb) - - local := []protocol.FileInfo{ - {Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - {Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - {Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - {Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - } - - remote := []protocol.FileInfo{ - {Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - {Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}}, - {Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}}, - {Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - } - - shouldNeed := []protocol.FileInfo{ - {Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}}, - {Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}}, - {Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - } - - replace(m, protocol.LocalDeviceID, local) - replace(m, remoteDevice0, remote) - - need := needList(t, m, protocol.LocalDeviceID) - - slices.SortFunc(need, compareByName) - slices.SortFunc(shouldNeed, compareByName) - - if fmt.Sprint(need) != fmt.Sprint(shouldNeed) { - t.Errorf("Need incorrect;\n%v !=\n%v", need, shouldNeed) - } - - checkNeed(t, m, protocol.LocalDeviceID, shouldNeed) -} - -func TestSequence(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - m := newFileSet(t, "test", ldb) - - local1 := []protocol.FileInfo{ - {Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - {Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - {Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - {Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - } - - local2 := []protocol.FileInfo{ - local1[0], - // [1] deleted - local1[2], - {Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}}, - {Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - } - - replace(m, protocol.LocalDeviceID, local1) - c0 := m.Sequence(protocol.LocalDeviceID) - - replace(m, protocol.LocalDeviceID, local2) - c1 := m.Sequence(protocol.LocalDeviceID) - if !(c1 > c0) { - t.Fatal("Local version number should have incremented") - } -} - -func TestListDropFolder(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - s0 := newFileSet(t, "test0", ldb) - local1 := []protocol.FileInfo{ - {Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - {Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - {Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - } - replace(s0, protocol.LocalDeviceID, local1) - - s1 := newFileSet(t, "test1", ldb) - local2 := []protocol.FileInfo{ - {Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}}, - {Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}}, - {Name: "f", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}}, - } - replace(s1, remoteDevice0, local2) - - // Check that we have both folders and their data is in the global list - - expectedFolderList := []string{"test0", "test1"} - actualFolderList := ldb.ListFolders() - if diff, equal := messagediff.PrettyDiff(expectedFolderList, actualFolderList); !equal { - t.Fatalf("FolderList mismatch. Diff:\n%s", diff) - } - if l := len(globalList(t, s0)); l != 3 { - t.Errorf("Incorrect global length %d != 3 for s0", l) - } - if l := len(globalList(t, s1)); l != 3 { - t.Errorf("Incorrect global length %d != 3 for s1", l) - } - - // Drop one of them and check that it's gone. - - db.DropFolder(ldb, "test1") - - expectedFolderList = []string{"test0"} - actualFolderList = ldb.ListFolders() - if diff, equal := messagediff.PrettyDiff(expectedFolderList, actualFolderList); !equal { - t.Fatalf("FolderList mismatch. Diff:\n%s", diff) - } - if l := len(globalList(t, s0)); l != 3 { - t.Errorf("Incorrect global length %d != 3 for s0", l) - } - if l := len(globalList(t, s1)); l != 0 { - t.Errorf("Incorrect global length %d != 0 for s1", l) - } -} - -func TestGlobalNeedWithInvalid(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - s := newFileSet(t, "test1", ldb) - - rem0 := fileList{ - protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)}, - protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, RawInvalid: true}, - protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)}, - protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: remoteDevice0.Short(), Value: 1002}}}}, - } - replace(s, remoteDevice0, rem0) - - rem1 := fileList{ - protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)}, - protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)}, - protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, RawInvalid: true}, - protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, RawInvalid: true, ModifiedS: 10}, - } - replace(s, remoteDevice1, rem1) - - total := fileList{ - // There's a valid copy of each file, so it should be merged - protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)}, - protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)}, - protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)}, - // in conflict and older, but still wins as the other is invalid - protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: remoteDevice0.Short(), Value: 1002}}}}, - } - - need := fileList(needList(t, s, protocol.LocalDeviceID)) - if fmt.Sprint(need) != fmt.Sprint(total) { - t.Errorf("Need incorrect;\n A: %v !=\n E: %v", need, total) - } - checkNeed(t, s, protocol.LocalDeviceID, total) - - global := fileList(globalList(t, s)) - if fmt.Sprint(global) != fmt.Sprint(total) { - t.Errorf("Global incorrect;\n A: %v !=\n E: %v", global, total) - } -} - -func TestLongPath(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - s := newFileSet(t, "test", ldb) - - var b bytes.Buffer - for i := 0; i < 100; i++ { - b.WriteString("012345678901234567890123456789012345678901234567890") - } - name := b.String() // 5000 characters - - local := []protocol.FileInfo{ - {Name: name, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - } - - replace(s, protocol.LocalDeviceID, local) - - gf := globalList(t, s) - if l := len(gf); l != 1 { - t.Fatalf("Incorrect len %d != 1 for global list", l) - } - if gf[0].Name != local[0].Name { - t.Errorf("Incorrect long filename;\n%q !=\n%q", - gf[0].Name, local[0].Name) - } -} - -func BenchmarkUpdateOneFile(b *testing.B) { - local0 := fileList{ - protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)}, - protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(2)}, - protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(3)}, - protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(4)}, - // A longer name is more realistic and causes more allocations - protocol.FileInfo{Name: "zajksdhaskjdh/askjdhaskjdashkajshd/kasjdhaskjdhaskdjhaskdjash/dkjashdaksjdhaskdjahskdjh", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(8)}, - } - - be, err := backend.Open("testdata/benchmarkupdate.db", backend.TuningAuto) - if err != nil { - b.Fatal(err) - } - ldb := newLowlevel(b, be) - defer func() { - ldb.Close() - os.RemoveAll("testdata/benchmarkupdate.db") - }() - - m := newFileSet(b, "test", ldb) - replace(m, protocol.LocalDeviceID, local0) - l := local0[4:5] - - for i := 0; i < b.N; i++ { - l[0].Version = l[0].Version.Update(myID) - m.Update(protocol.LocalDeviceID, local0) - } - - b.ReportAllocs() -} - -func TestIndexID(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - s := newFileSet(t, "test", ldb) - - // The Index ID for some random device is zero by default. - id := s.IndexID(remoteDevice0) - if id != 0 { - t.Errorf("index ID for remote device should default to zero, not %d", id) - } - - // The Index ID for someone else should be settable - s.SetIndexID(remoteDevice0, 42) - id = s.IndexID(remoteDevice0) - if id != 42 { - t.Errorf("index ID for remote device should be remembered; got %d, expected %d", id, 42) - } - - // Our own index ID should be generated randomly. - id = s.IndexID(protocol.LocalDeviceID) - if id == 0 { - t.Errorf("index ID for local device should be random, not zero") - } - t.Logf("random index ID is 0x%016x", id) - - // But of course always the same after that. - again := s.IndexID(protocol.LocalDeviceID) - if again != id { - t.Errorf("index ID changed; %d != %d", again, id) - } -} - -func TestDropFiles(t *testing.T) { - ldb := newLowlevelMemory(t) - - m := newFileSet(t, "test", ldb) - - local0 := fileList{ - protocol.FileInfo{Name: "a", Sequence: 1, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)}, - protocol.FileInfo{Name: "b", Sequence: 2, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(2)}, - protocol.FileInfo{Name: "c", Sequence: 3, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(3)}, - protocol.FileInfo{Name: "d", Sequence: 4, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(4)}, - protocol.FileInfo{Name: "z", Sequence: 5, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(8)}, - } - - remote0 := fileList{ - protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)}, - protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(2)}, - protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(5)}, - } - - // Insert files - - m.Update(protocol.LocalDeviceID, local0) - m.Update(remoteDevice0, remote0) - - // Check that they're there - - h := haveList(t, m, protocol.LocalDeviceID) - if len(h) != len(local0) { - t.Errorf("Incorrect number of files after update, %d != %d", len(h), len(local0)) - } - - h = haveList(t, m, remoteDevice0) - if len(h) != len(remote0) { - t.Errorf("Incorrect number of files after update, %d != %d", len(h), len(local0)) - } - - g := globalList(t, m) - if len(g) != len(local0) { - // local0 covers all files - t.Errorf("Incorrect global files after update, %d != %d", len(g), len(local0)) - } - - // Drop the local files and recheck - - m.Drop(protocol.LocalDeviceID) - - h = haveList(t, m, protocol.LocalDeviceID) - if len(h) != 0 { - t.Errorf("Incorrect number of files after drop, %d != %d", len(h), 0) - } - - h = haveList(t, m, remoteDevice0) - if len(h) != len(remote0) { - t.Errorf("Incorrect number of files after update, %d != %d", len(h), len(local0)) - } - - g = globalList(t, m) - if len(g) != len(remote0) { - // the ones in remote0 remain - t.Errorf("Incorrect global files after update, %d != %d", len(g), len(remote0)) - } -} - -func TestIssue4701(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - s := newFileSet(t, "test", ldb) - - localHave := fileList{ - protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, LocalFlags: protocol.FlagLocalIgnored}, - } - - s.Update(protocol.LocalDeviceID, localHave) - - if c := localSize(t, s); c.Files != 1 { - t.Errorf("Expected 1 local file, got %v", c.Files) - } - if c := globalSize(t, s); c.Files != 1 { - t.Errorf("Expected 1 global file, got %v", c.Files) - } - - localHave[1].LocalFlags = 0 - s.Update(protocol.LocalDeviceID, localHave) - - if c := localSize(t, s); c.Files != 2 { - t.Errorf("Expected 2 local files, got %v", c.Files) - } - if c := globalSize(t, s); c.Files != 2 { - t.Errorf("Expected 2 global files, got %v", c.Files) - } - - localHave[0].LocalFlags = protocol.FlagLocalIgnored - localHave[1].LocalFlags = protocol.FlagLocalIgnored - s.Update(protocol.LocalDeviceID, localHave) - - if c := localSize(t, s); c.Files != 0 { - t.Errorf("Expected 0 local files, got %v", c.Files) - } - if c := globalSize(t, s); c.Files != 0 { - t.Errorf("Expected 0 global files, got %v", c.Files) - } -} - -func TestWithHaveSequence(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - folder := "test" - s := newFileSet(t, folder, ldb) - - // The files must not be in alphabetical order - localHave := fileList{ - protocol.FileInfo{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, RawInvalid: true}, - protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)}, - protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)}, - protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)}, - protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), RawInvalid: true}, - } - - replace(s, protocol.LocalDeviceID, localHave) - - i := 2 - snap := snapshot(t, s) - defer snap.Release() - snap.WithHaveSequence(int64(i), func(fi protocol.FileInfo) bool { - if !fi.IsEquivalent(localHave[i-1], 0) { - t.Fatalf("Got %v\nExpected %v", fi, localHave[i-1]) - } - i++ - return true - }) -} - -func TestStressWithHaveSequence(t *testing.T) { - // This races two loops against each other: one that continuously does - // updates, and one that continuously does sequence walks. The test fails - // if the sequence walker sees a discontinuity. - - if testing.Short() { - t.Skip("Takes a long time") - } - - ldb := newLowlevelMemory(t) - defer ldb.Close() - - folder := "test" - s := newFileSet(t, folder, ldb) - - var localHave []protocol.FileInfo - for i := 0; i < 100; i++ { - localHave = append(localHave, protocol.FileInfo{Name: fmt.Sprintf("file%d", i), Blocks: genBlocks(i * 10)}) - } - - done := make(chan struct{}) - t0 := time.Now() - go func() { - for time.Since(t0) < 10*time.Second { - for j, f := range localHave { - localHave[j].Version = f.Version.Update(42) - } - - s.Update(protocol.LocalDeviceID, localHave) - } - close(done) - }() - - var prevSeq int64 -loop: - for { - select { - case <-done: - break loop - default: - } - snap := snapshot(t, s) - snap.WithHaveSequence(prevSeq+1, func(fi protocol.FileInfo) bool { - if fi.SequenceNo() < prevSeq+1 { - t.Fatal("Skipped ", prevSeq+1, fi.SequenceNo()) - } - prevSeq = fi.SequenceNo() - return true - }) - snap.Release() - } -} - -func TestIssue4925(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - folder := "test" - s := newFileSet(t, folder, ldb) - - localHave := fileList{ - protocol.FileInfo{Name: "dir"}, - protocol.FileInfo{Name: "dir.file"}, - protocol.FileInfo{Name: "dir/file"}, - } - - replace(s, protocol.LocalDeviceID, localHave) - - for _, prefix := range []string{"dir", "dir/"} { - pl := haveListPrefixed(t, s, protocol.LocalDeviceID, prefix) - if l := len(pl); l != 2 { - t.Errorf("Expected 2, got %v local items below %v", l, prefix) - } - pl = globalListPrefixed(t, s, prefix) - if l := len(pl); l != 2 { - t.Errorf("Expected 2, got %v global items below %v", l, prefix) - } - } -} - -func TestMoveGlobalBack(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - folder := "test" - file := "foo" - s := newFileSet(t, folder, ldb) - - localHave := fileList{{Name: file, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1}}}, Blocks: genBlocks(1), ModifiedS: 10, Size: 1}} - remote0Have := fileList{{Name: file, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1}, {ID: remoteDevice0.Short(), Value: 1}}}, Blocks: genBlocks(2), ModifiedS: 0, Size: 2}} - - s.Update(protocol.LocalDeviceID, localHave) - s.Update(remoteDevice0, remote0Have) - - if need := needList(t, s, protocol.LocalDeviceID); len(need) != 1 { - t.Error("Expected 1 local need, got", need) - } else if !need[0].IsEquivalent(remote0Have[0], 0) { - t.Errorf("Local need incorrect;\n A: %v !=\n E: %v", need[0], remote0Have[0]) - } - checkNeed(t, s, protocol.LocalDeviceID, remote0Have[:1]) - - if need := needList(t, s, remoteDevice0); len(need) != 0 { - t.Error("Expected no need for remote 0, got", need) - } - checkNeed(t, s, remoteDevice0, nil) - - ls := localSize(t, s) - if haveBytes := localHave[0].Size; ls.Bytes != haveBytes { - t.Errorf("Incorrect LocalSize bytes; %d != %d", ls.Bytes, haveBytes) - } - - gs := globalSize(t, s) - if globalBytes := remote0Have[0].Size; gs.Bytes != globalBytes { - t.Errorf("Incorrect GlobalSize bytes; %d != %d", gs.Bytes, globalBytes) - } - - // That's what happens when something becomes unignored or something. - // In any case it will be moved back from first spot in the global list - // which is the scenario to be tested here. - remote0Have[0].Version = remote0Have[0].Version.Update(remoteDevice0.Short()).DropOthers(remoteDevice0.Short()) - s.Update(remoteDevice0, remote0Have) - - if need := needList(t, s, remoteDevice0); len(need) != 1 { - t.Error("Expected 1 need for remote 0, got", need) - } else if !need[0].IsEquivalent(localHave[0], 0) { - t.Errorf("Need for remote 0 incorrect;\n A: %v !=\n E: %v", need[0], localHave[0]) - } - checkNeed(t, s, remoteDevice0, localHave[:1]) - - if need := needList(t, s, protocol.LocalDeviceID); len(need) != 0 { - t.Error("Expected no local need, got", need) - } - checkNeed(t, s, protocol.LocalDeviceID, nil) - - ls = localSize(t, s) - if haveBytes := localHave[0].Size; ls.Bytes != haveBytes { - t.Errorf("Incorrect LocalSize bytes; %d != %d", ls.Bytes, haveBytes) - } - - gs = globalSize(t, s) - if globalBytes := localHave[0].Size; gs.Bytes != globalBytes { - t.Errorf("Incorrect GlobalSize bytes; %d != %d", gs.Bytes, globalBytes) - } -} - -// TestIssue5007 checks, that updating the local device with an invalid file -// info with the newest version does indeed remove that file from the list of -// needed files. -// https://github.com/syncthing/syncthing/issues/5007 -func TestIssue5007(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - folder := "test" - file := "foo" - s := newFileSet(t, folder, ldb) - - fs := fileList{{Name: file, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1}}}}} - - s.Update(remoteDevice0, fs) - - if need := needList(t, s, protocol.LocalDeviceID); len(need) != 1 { - t.Fatal("Expected 1 local need, got", need) - } else if !need[0].IsEquivalent(fs[0], 0) { - t.Fatalf("Local need incorrect;\n A: %v !=\n E: %v", need[0], fs[0]) - } - checkNeed(t, s, protocol.LocalDeviceID, fs[:1]) - - fs[0].LocalFlags = protocol.FlagLocalIgnored - s.Update(protocol.LocalDeviceID, fs) - - if need := needList(t, s, protocol.LocalDeviceID); len(need) != 0 { - t.Fatal("Expected no local need, got", need) - } - checkNeed(t, s, protocol.LocalDeviceID, nil) -} - -// TestNeedDeleted checks that a file that doesn't exist locally isn't needed -// when the global file is deleted. -func TestNeedDeleted(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - folder := "test" - file := "foo" - s := newFileSet(t, folder, ldb) - - fs := fileList{{Name: file, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1}}}, Deleted: true}} - - s.Update(remoteDevice0, fs) - - if need := needList(t, s, protocol.LocalDeviceID); len(need) != 0 { - t.Fatal("Expected no local need, got", need) - } - checkNeed(t, s, protocol.LocalDeviceID, nil) - - fs[0].Deleted = false - fs[0].Version = fs[0].Version.Update(remoteDevice0.Short()) - s.Update(remoteDevice0, fs) - - if need := needList(t, s, protocol.LocalDeviceID); len(need) != 1 { - t.Fatal("Expected 1 local need, got", need) - } else if !need[0].IsEquivalent(fs[0], 0) { - t.Fatalf("Local need incorrect;\n A: %v !=\n E: %v", need[0], fs[0]) - } - checkNeed(t, s, protocol.LocalDeviceID, fs[:1]) - - fs[0].Deleted = true - fs[0].Version = fs[0].Version.Update(remoteDevice0.Short()) - s.Update(remoteDevice0, fs) - - if need := needList(t, s, protocol.LocalDeviceID); len(need) != 0 { - t.Fatal("Expected no local need, got", need) - } - checkNeed(t, s, protocol.LocalDeviceID, nil) -} - -func TestReceiveOnlyAccounting(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - folder := "test" - s := newFileSet(t, folder, ldb) - - local := protocol.DeviceID{1} - remote := protocol.DeviceID{2} - - // Three files that have been created by the remote device - - version := protocol.Vector{Counters: []protocol.Counter{{ID: remote.Short(), Value: 1}}} - files := fileList{ - protocol.FileInfo{Name: "f1", Size: 10, Sequence: 1, Version: version}, - protocol.FileInfo{Name: "f2", Size: 10, Sequence: 1, Version: version}, - protocol.FileInfo{Name: "f3", Size: 10, Sequence: 1, Version: version}, - } - - // We have synced them locally - - replace(s, protocol.LocalDeviceID, files) - replace(s, remote, files) - - if n := localSize(t, s).Files; n != 3 { - t.Fatal("expected 3 local files initially, not", n) - } - if n := localSize(t, s).Bytes; n != 30 { - t.Fatal("expected 30 local bytes initially, not", n) - } - if n := globalSize(t, s).Files; n != 3 { - t.Fatal("expected 3 global files initially, not", n) - } - if n := globalSize(t, s).Bytes; n != 30 { - t.Fatal("expected 30 global bytes initially, not", n) - } - if n := receiveOnlyChangedSize(t, s).Files; n != 0 { - t.Fatal("expected 0 receive only changed files initially, not", n) - } - if n := receiveOnlyChangedSize(t, s).Bytes; n != 0 { - t.Fatal("expected 0 receive only changed bytes initially, not", n) - } - - // Detected a local change in a receive only folder - - changed := files[0] - changed.Version = changed.Version.Update(local.Short()) - changed.Size = 100 - changed.ModifiedBy = local.Short() - changed.LocalFlags = protocol.FlagLocalReceiveOnly - s.Update(protocol.LocalDeviceID, []protocol.FileInfo{changed}) - - // Check that we see the files - - if n := localSize(t, s).Files; n != 3 { - t.Fatal("expected 3 local files after local change, not", n) - } - if n := localSize(t, s).Bytes; n != 120 { - t.Fatal("expected 120 local bytes after local change, not", n) - } - if n := globalSize(t, s).Files; n != 3 { - t.Fatal("expected 3 global files after local change, not", n) - } - if n := globalSize(t, s).Bytes; n != 30 { - t.Fatal("expected 30 global files after local change, not", n) - } - if n := receiveOnlyChangedSize(t, s).Files; n != 1 { - t.Fatal("expected 1 receive only changed file after local change, not", n) - } - if n := receiveOnlyChangedSize(t, s).Bytes; n != 100 { - t.Fatal("expected 100 receive only changed bytes after local change, not", n) - } - - // Fake a revert. That's a two step process, first converting our - // changed file into a less preferred variant, then pulling down the old - // version. - - changed.Version = protocol.Vector{} - changed.LocalFlags &^= protocol.FlagLocalReceiveOnly - s.Update(protocol.LocalDeviceID, []protocol.FileInfo{changed}) - - s.Update(protocol.LocalDeviceID, []protocol.FileInfo{files[0]}) - - // Check that we see the files, same data as initially - - if n := localSize(t, s).Files; n != 3 { - t.Fatal("expected 3 local files after revert, not", n) - } - if n := localSize(t, s).Bytes; n != 30 { - t.Fatal("expected 30 local bytes after revert, not", n) - } - if n := globalSize(t, s).Files; n != 3 { - t.Fatal("expected 3 global files after revert, not", n) - } - if n := globalSize(t, s).Bytes; n != 30 { - t.Fatal("expected 30 global bytes after revert, not", n) - } - if n := receiveOnlyChangedSize(t, s).Files; n != 0 { - t.Fatal("expected 0 receive only changed files after revert, not", n) - } - if n := receiveOnlyChangedSize(t, s).Bytes; n != 0 { - t.Fatal("expected 0 receive only changed bytes after revert, not", n) - } -} - -func TestNeedAfterUnignore(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - folder := "test" - file := "foo" - s := newFileSet(t, folder, ldb) - - remID := remoteDevice0.Short() - - // Initial state: Devices in sync, locally ignored - local := protocol.FileInfo{Name: file, Version: protocol.Vector{Counters: []protocol.Counter{{ID: remID, Value: 1}, {ID: myID, Value: 1}}}, ModifiedS: 10} - local.SetIgnored() - remote := protocol.FileInfo{Name: file, Version: protocol.Vector{Counters: []protocol.Counter{{ID: remID, Value: 1}, {ID: myID, Value: 1}}}, ModifiedS: 10} - s.Update(protocol.LocalDeviceID, fileList{local}) - s.Update(remoteDevice0, fileList{remote}) - - // Unignore locally -> conflicting changes. Remote is newer, thus winning. - local.Version = local.Version.Update(myID) - local.Version = local.Version.DropOthers(myID) - local.LocalFlags = 0 - local.ModifiedS = 0 - s.Update(protocol.LocalDeviceID, fileList{local}) - - if need := needList(t, s, protocol.LocalDeviceID); len(need) != 1 { - t.Fatal("Expected one local need, got", need) - } else if !need[0].IsEquivalent(remote, 0) { - t.Fatalf("Got %v, expected %v", need[0], remote) - } - checkNeed(t, s, protocol.LocalDeviceID, []protocol.FileInfo{remote}) -} - -func TestRemoteInvalidNotAccounted(t *testing.T) { - // Remote files with the invalid bit should not count. - - ldb := newLowlevelMemory(t) - defer ldb.Close() - s := newFileSet(t, "test", ldb) - - files := []protocol.FileInfo{ - {Name: "a", Size: 1234, Sequence: 42, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}}, // valid, should count - {Name: "b", Size: 1234, Sequence: 43, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, RawInvalid: true}, // invalid, doesn't count - } - s.Update(remoteDevice0, files) - - global := globalSize(t, s) - if global.Files != 1 { - t.Error("Expected one file in global size, not", global.Files) - } - if global.Bytes != 1234 { - t.Error("Expected 1234 bytes in global size, not", global.Bytes) - } -} - -func TestNeedWithNewerInvalid(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - s := newFileSet(t, "default", ldb) - - rem0ID := remoteDevice0.Short() - rem1ID := remoteDevice1.Short() - - // Initial state: file present on rem0 and rem1, but not locally. - file := protocol.FileInfo{Name: "foo"} - file.Version = file.Version.Update(rem0ID) - s.Update(remoteDevice0, fileList{file}) - s.Update(remoteDevice1, fileList{file}) - - need := needList(t, s, protocol.LocalDeviceID) - if len(need) != 1 { - t.Fatal("Locally missing file should be needed") - } - if !need[0].IsEquivalent(file, 0) { - t.Fatalf("Got needed file %v, expected %v", need[0], file) - } - checkNeed(t, s, protocol.LocalDeviceID, []protocol.FileInfo{file}) - - // rem1 sends an invalid file with increased version - inv := file - inv.Version = inv.Version.Update(rem1ID) - inv.RawInvalid = true - s.Update(remoteDevice1, fileList{inv}) - - // We still have an old file, we need the newest valid file - need = needList(t, s, protocol.LocalDeviceID) - if len(need) != 1 { - t.Fatal("Locally missing file should be needed regardless of invalid files") - } - if !need[0].IsEquivalent(file, 0) { - t.Fatalf("Got needed file %v, expected %v", need[0], file) - } - checkNeed(t, s, protocol.LocalDeviceID, []protocol.FileInfo{file}) -} - -func TestNeedAfterDeviceRemove(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - file := "foo" - s := newFileSet(t, "test", ldb) - - fs := fileList{{Name: file, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1}}}}} - - s.Update(protocol.LocalDeviceID, fs) - - fs[0].Version = fs[0].Version.Update(myID) - - s.Update(remoteDevice0, fs) - - if need := needList(t, s, protocol.LocalDeviceID); len(need) != 1 { - t.Fatal("Expected one local need, got", need) - } - - s.Drop(remoteDevice0) - - if need := needList(t, s, protocol.LocalDeviceID); len(need) != 0 { - t.Fatal("Expected no local need, got", need) - } - checkNeed(t, s, protocol.LocalDeviceID, nil) -} - -func TestCaseSensitive(t *testing.T) { - // Normal case sensitive lookup should work - - ldb := newLowlevelMemory(t) - defer ldb.Close() - s := newFileSet(t, "test", ldb) - - local := []protocol.FileInfo{ - {Name: filepath.FromSlash("D1/f1"), Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - {Name: filepath.FromSlash("F1"), Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - {Name: filepath.FromSlash("d1/F1"), Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - {Name: filepath.FromSlash("d1/f1"), Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - {Name: filepath.FromSlash("f1"), Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - } - - replace(s, protocol.LocalDeviceID, local) - - gf := globalList(t, s) - if l := len(gf); l != len(local) { - t.Fatalf("Incorrect len %d != %d for global list", l, len(local)) - } - for i := range local { - if gf[i].Name != local[i].Name { - t.Errorf("Incorrect filename;\n%q !=\n%q", - gf[i].Name, local[i].Name) - } - } -} - -func TestSequenceIndex(t *testing.T) { - // This test attempts to verify correct operation of the sequence index. - - // It's a stress test and needs to run for a long time, but we don't - // really have time for that in normal builds. - runtime := time.Minute - if testing.Short() { - runtime = time.Second - } - - // Set up a db and a few files that we will manipulate. - - ldb := newLowlevelMemory(t) - defer ldb.Close() - s := newFileSet(t, "test", ldb) - - local := []protocol.FileInfo{ - {Name: filepath.FromSlash("banana"), Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - {Name: filepath.FromSlash("pineapple"), Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - {Name: filepath.FromSlash("orange"), Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - {Name: filepath.FromSlash("apple"), Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - {Name: filepath.FromSlash("jackfruit"), Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - } - - // Start a background routine that makes updates to these files as fast - // as it can. We always update the same files in the same order. - - done := make(chan struct{}) - defer close(done) - - go func() { - for { - select { - case <-done: - return - default: - } - - for i := range local { - local[i].Version = local[i].Version.Update(42) - } - s.Update(protocol.LocalDeviceID, local) - } - }() - - // Start a routine to walk the sequence index and inspect the result. - - seen := make(map[string]protocol.FileInfo) - latest := make([]protocol.FileInfo, 0, len(local)) - var seq int64 - t0 := time.Now() - - for time.Since(t0) < runtime { - // Walk the changes since our last iteration. This should give is - // one instance each of the files that are changed all the time, or - // a subset of those files if we manage to run before a complete - // update has happened since our last iteration. - latest = latest[:0] - snap := snapshot(t, s) - snap.WithHaveSequence(seq+1, func(f protocol.FileInfo) bool { - seen[f.FileName()] = f - latest = append(latest, f) - seq = f.SequenceNo() - return true - }) - snap.Release() - - // Calculate the spread in sequence number. - var max, min int64 - for _, v := range seen { - s := v.SequenceNo() - if max == 0 || max < s { - max = s - } - if min == 0 || min > s { - min = s - } - } - - // We shouldn't see a spread larger than the number of files, as - // that would mean we have missed updates. For example, if we were - // to see the following: - // - // banana N - // pineapple N+1 - // orange N+2 - // apple N+10 - // jackfruit N+11 - // - // that would mean that there have been updates to banana, pineapple - // and orange that we didn't see in this pass. If those files aren't - // updated again, those updates are permanently lost. - if max-min > int64(len(local)) { - for _, v := range seen { - t.Log("seen", v.FileName(), v.SequenceNo()) - } - for _, v := range latest { - t.Log("latest", v.FileName(), v.SequenceNo()) - } - t.Fatal("large spread") - } - time.Sleep(time.Millisecond) - } -} - -func TestIgnoreAfterReceiveOnly(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - file := "foo" - s := newFileSet(t, "test", ldb) - - fs := fileList{{ - Name: file, - Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1}}}, - LocalFlags: protocol.FlagLocalReceiveOnly, - }} - - s.Update(protocol.LocalDeviceID, fs) - - fs[0].LocalFlags = protocol.FlagLocalIgnored - - s.Update(protocol.LocalDeviceID, fs) - - snap := snapshot(t, s) - defer snap.Release() - if f, ok := snap.Get(protocol.LocalDeviceID, file); !ok { - t.Error("File missing in db") - } else if f.IsReceiveOnlyChanged() { - t.Error("File is still receive-only changed") - } else if !f.IsIgnored() { - t.Error("File is not ignored") - } -} - -// https://github.com/syncthing/syncthing/issues/6650 -func TestUpdateWithOneFileTwice(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - file := "foo" - s := newFileSet(t, "test", ldb) - - fs := fileList{{ - Name: file, - Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1}}}, - Sequence: 1, - }} - - s.Update(protocol.LocalDeviceID, fs) - - fs = append(fs, fs[0]) - for i := range fs { - fs[i].Sequence++ - fs[i].Version = fs[i].Version.Update(myID) - } - fs[1].Sequence++ - fs[1].Version = fs[1].Version.Update(myID) - - s.Update(protocol.LocalDeviceID, fs) - - snap := snapshot(t, s) - defer snap.Release() - count := 0 - snap.WithHaveSequence(0, func(_ protocol.FileInfo) bool { - count++ - return true - }) - if count != 1 { - t.Error("Expected to have one file, got", count) - } -} - -// https://github.com/syncthing/syncthing/issues/6668 -func TestNeedRemoteOnly(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - s := newFileSet(t, "test", ldb) - - remote0Have := fileList{ - protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)}, - } - s.Update(remoteDevice0, remote0Have) - - need := needSize(t, s, remoteDevice0) - if !need.Equal(db.Counts{}) { - t.Error("Expected nothing needed, got", need) - } -} - -// https://github.com/syncthing/syncthing/issues/6784 -func TestNeedRemoteAfterReset(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - s := newFileSet(t, "test", ldb) - - files := fileList{ - protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)}, - } - s.Update(protocol.LocalDeviceID, files) - s.Update(remoteDevice0, files) - - need := needSize(t, s, remoteDevice0) - if !need.Equal(db.Counts{}) { - t.Error("Expected nothing needed, got", need) - } - - s.Drop(remoteDevice0) - - need = needSize(t, s, remoteDevice0) - if exp := (db.Counts{Files: 1}); !need.Equal(exp) { - t.Errorf("Expected %v, got %v", exp, need) - } -} - -// https://github.com/syncthing/syncthing/issues/6850 -func TestIgnoreLocalChanged(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - s := newFileSet(t, "test", ldb) - - // Add locally changed file - files := fileList{ - protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2), LocalFlags: protocol.FlagLocalReceiveOnly}, - } - s.Update(protocol.LocalDeviceID, files) - - if c := globalSize(t, s).Files; c != 0 { - t.Error("Expected no global file, got", c) - } - if c := localSize(t, s).Files; c != 1 { - t.Error("Expected one local file, got", c) - } - - // Change file to ignored - files[0].LocalFlags = protocol.FlagLocalIgnored - s.Update(protocol.LocalDeviceID, files) - - if c := globalSize(t, s).Files; c != 0 { - t.Error("Expected no global file, got", c) - } - if c := localSize(t, s).Files; c != 0 { - t.Error("Expected no local file, got", c) - } -} - -// Dropping the index ID on Drop is bad, because Drop gets called when receiving -// an Index (as opposed to an IndexUpdate), and we don't want to loose the index -// ID when that happens. -func TestNoIndexIDResetOnDrop(t *testing.T) { - ldb := newLowlevelMemory(t) - defer ldb.Close() - - s := newFileSet(t, "test", ldb) - - s.SetIndexID(remoteDevice0, 1) - s.Drop(remoteDevice0) - if got := s.IndexID(remoteDevice0); got != 1 { - t.Errorf("Expected unchanged (%v), got %v", 1, got) - } -} - -func TestConcurrentIndexID(t *testing.T) { - done := make(chan struct{}) - var ids [2]protocol.IndexID - setID := func(s *db.FileSet, i int) { - ids[i] = s.IndexID(protocol.LocalDeviceID) - done <- struct{}{} - } - - max := 100 - if testing.Short() { - max = 10 - } - for i := 0; i < max; i++ { - ldb := newLowlevelMemory(t) - s := newFileSet(t, "test", ldb) - go setID(s, 0) - go setID(s, 1) - <-done - <-done - ldb.Close() - if ids[0] != ids[1] { - t.Fatalf("IDs differ after %v rounds", i) - } - } -} - -func TestNeedRemoveLastValid(t *testing.T) { - db := newLowlevelMemory(t) - defer db.Close() - - folder := "test" - - fs := newFileSet(t, folder, db) - - files := []protocol.FileInfo{ - {Name: "foo", Version: protocol.Vector{}.Update(myID), Sequence: 1}, - } - fs.Update(remoteDevice0, files) - files[0].Version = files[0].Version.Update(myID) - fs.Update(remoteDevice1, files) - files[0].LocalFlags = protocol.FlagLocalIgnored - fs.Update(protocol.LocalDeviceID, files) - - snap := snapshot(t, fs) - c := snap.NeedSize(remoteDevice0) - if c.Files != 1 { - t.Errorf("Expected 1 needed files initially, got %v", c.Files) - } - snap.Release() - - fs.Drop(remoteDevice1) - - snap = snapshot(t, fs) - c = snap.NeedSize(remoteDevice0) - if c.Files != 0 { - t.Errorf("Expected no needed files, got %v", c.Files) - } - snap.Release() -} - -func replace(fs *db.FileSet, device protocol.DeviceID, files []protocol.FileInfo) { - fs.Drop(device) - fs.Update(device, files) -} - -func localSize(t testing.TB, fs *db.FileSet) db.Counts { - snap := snapshot(t, fs) - defer snap.Release() - return snap.LocalSize() -} - -func globalSize(t testing.TB, fs *db.FileSet) db.Counts { - snap := snapshot(t, fs) - defer snap.Release() - return snap.GlobalSize() -} - -func needSize(t testing.TB, fs *db.FileSet, id protocol.DeviceID) db.Counts { - snap := snapshot(t, fs) - defer snap.Release() - return snap.NeedSize(id) -} - -func receiveOnlyChangedSize(t testing.TB, fs *db.FileSet) db.Counts { - snap := snapshot(t, fs) - defer snap.Release() - return snap.ReceiveOnlyChangedSize() -} - -func filesToCounts(files []protocol.FileInfo) db.Counts { - cp := db.Counts{} - for _, f := range files { - switch { - case f.IsDeleted(): - cp.Deleted++ - case f.IsDirectory() && !f.IsSymlink(): - cp.Directories++ - case f.IsSymlink(): - cp.Symlinks++ - default: - cp.Files++ - } - cp.Bytes += f.FileSize() - } - return cp -} - -func checkNeed(t testing.TB, s *db.FileSet, dev protocol.DeviceID, expected []protocol.FileInfo) { - t.Helper() - counts := needSize(t, s, dev) - if exp := filesToCounts(expected); !exp.Equal(counts) { - t.Errorf("Count incorrect (%v): expected %v, got %v", dev, exp, counts) - } -} - -func newLowlevel(t testing.TB, backend backend.Backend) *db.Lowlevel { - t.Helper() - ll, err := db.NewLowlevel(backend, events.NoopLogger) - if err != nil { - t.Fatal(err) - } - return ll -} - -func newLowlevelMemory(t testing.TB) *db.Lowlevel { - return newLowlevel(t, backend.OpenMemory()) -} - -func newFileSet(t testing.TB, folder string, ll *db.Lowlevel) *db.FileSet { - t.Helper() - fset, err := db.NewFileSet(folder, ll) - if err != nil { - t.Fatal(err) - } - return fset -} - -func snapshot(t testing.TB, fset *db.FileSet) *db.Snapshot { - t.Helper() - snap, err := fset.Snapshot() - if err != nil { - t.Fatal(err) - } - return snap -} diff --git a/lib/db/smallindex_test.go b/lib/db/smallindex_test.go deleted file mode 100644 index c47a685a0..000000000 --- a/lib/db/smallindex_test.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (C) 2018 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package db - -import ( - "testing" -) - -func TestSmallIndex(t *testing.T) { - db := newLowlevelMemory(t) - idx := newSmallIndex(db, []byte{12, 34}) - - // ID zero should be unallocated - if val, ok := idx.Val(0); ok || val != nil { - t.Fatal("Unexpected return for nonexistent ID 0") - } - - // A new key should get ID zero - if id, err := idx.ID([]byte("hello")); err != nil { - t.Fatal(err) - } else if id != 0 { - t.Fatal("Expected 0, not", id) - } - // Looking up ID zero should work - if val, ok := idx.Val(0); !ok || string(val) != "hello" { - t.Fatalf(`Expected true, "hello", not %v, %q`, ok, val) - } - - // Delete the key - idx.Delete([]byte("hello")) - - // Next ID should be one - if id, err := idx.ID([]byte("key2")); err != nil { - t.Fatal(err) - } else if id != 1 { - t.Fatal("Expected 1, not", id) - } - - // Now lets create a new index instance based on what's actually serialized to the database. - idx = newSmallIndex(db, []byte{12, 34}) - - // Status should be about the same as before. - if val, ok := idx.Val(0); ok || val != nil { - t.Fatal("Unexpected return for deleted ID 0") - } - if id, err := idx.ID([]byte("key2")); err != nil { - t.Fatal(err) - } else if id != 1 { - t.Fatal("Expected 1, not", id) - } - - // Setting "hello" again should get us ID 2, not 0 as it was originally. - if id, err := idx.ID([]byte("hello")); err != nil { - t.Fatal(err) - } else if id != 2 { - t.Fatal("Expected 2, not", id) - } -} diff --git a/lib/db/structs.go b/lib/db/structs.go deleted file mode 100644 index 69d602112..000000000 --- a/lib/db/structs.go +++ /dev/null @@ -1,363 +0,0 @@ -// Copyright (C) 2014 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package db - -import ( - "bytes" - "fmt" - "strings" - - "google.golang.org/protobuf/proto" - - "github.com/syncthing/syncthing/internal/gen/dbproto" - "github.com/syncthing/syncthing/lib/protocol" -) - -type CountsSet struct { - Counts []Counts - Created int64 // unix nanos -} - -type Counts struct { - Files int - Directories int - Symlinks int - Deleted int - Bytes int64 - Sequence int64 // zero for the global state - DeviceID protocol.DeviceID // device ID for remote devices, or special values for local/global - LocalFlags uint32 // the local flag for this count bucket -} - -func (c Counts) toWire() *dbproto.Counts { - return &dbproto.Counts{ - Files: int32(c.Files), - Directories: int32(c.Directories), - Symlinks: int32(c.Symlinks), - Deleted: int32(c.Deleted), - Bytes: c.Bytes, - Sequence: c.Sequence, - DeviceId: c.DeviceID[:], - LocalFlags: c.LocalFlags, - } -} - -func countsFromWire(w *dbproto.Counts) Counts { - return Counts{ - Files: int(w.Files), - Directories: int(w.Directories), - Symlinks: int(w.Symlinks), - Deleted: int(w.Deleted), - Bytes: w.Bytes, - Sequence: w.Sequence, - DeviceID: protocol.DeviceID(w.DeviceId), - LocalFlags: w.LocalFlags, - } -} - -func (c Counts) Add(other Counts) Counts { - return Counts{ - Files: c.Files + other.Files, - Directories: c.Directories + other.Directories, - Symlinks: c.Symlinks + other.Symlinks, - Deleted: c.Deleted + other.Deleted, - Bytes: c.Bytes + other.Bytes, - Sequence: c.Sequence + other.Sequence, - DeviceID: protocol.EmptyDeviceID, - LocalFlags: c.LocalFlags | other.LocalFlags, - } -} - -func (c Counts) TotalItems() int { - return c.Files + c.Directories + c.Symlinks + c.Deleted -} - -func (c Counts) String() string { - var flags strings.Builder - if c.LocalFlags&needFlag != 0 { - flags.WriteString("Need") - } - if c.LocalFlags&protocol.FlagLocalIgnored != 0 { - flags.WriteString("Ignored") - } - if c.LocalFlags&protocol.FlagLocalMustRescan != 0 { - flags.WriteString("Rescan") - } - if c.LocalFlags&protocol.FlagLocalReceiveOnly != 0 { - flags.WriteString("Recvonly") - } - if c.LocalFlags&protocol.FlagLocalUnsupported != 0 { - flags.WriteString("Unsupported") - } - if c.LocalFlags != 0 { - flags.WriteString(fmt.Sprintf("(%x)", c.LocalFlags)) - } - if flags.Len() == 0 { - flags.WriteString("---") - } - return fmt.Sprintf("{Device:%v, Files:%d, Dirs:%d, Symlinks:%d, Del:%d, Bytes:%d, Seq:%d, Flags:%s}", c.DeviceID, c.Files, c.Directories, c.Symlinks, c.Deleted, c.Bytes, c.Sequence, flags.String()) -} - -// Equal compares the numbers only, not sequence/dev/flags. -func (c Counts) Equal(o Counts) bool { - return c.Files == o.Files && c.Directories == o.Directories && c.Symlinks == o.Symlinks && c.Deleted == o.Deleted && c.Bytes == o.Bytes -} - -// update brings the VersionList up to date with file. It returns the updated -// VersionList, a device that has the global/newest version, a device that previously -// had the global/newest version, a boolean indicating if the global version has -// changed and if any error occurred (only possible in db interaction). -func vlUpdate(vl *dbproto.VersionList, folder, device []byte, file protocol.FileInfo, t readOnlyTransaction) (*dbproto.FileVersion, *dbproto.FileVersion, *dbproto.FileVersion, bool, bool, bool, error) { - if len(vl.Versions) == 0 { - nv := newFileVersion(device, file.FileVersion(), file.IsInvalid(), file.IsDeleted()) - vl.Versions = append(vl.Versions, nv) - return nv, nil, nil, false, false, true, nil - } - - // Get the current global (before updating) - oldFV, haveOldGlobal := vlGetGlobal(vl) - oldFV = fvCopy(oldFV) - - // Remove ourselves first - removedFV, haveRemoved, _ := vlPop(vl, device) - // Find position and insert the file - err := vlInsert(vl, folder, device, file, t) - if err != nil { - return nil, nil, nil, false, false, false, err - } - - newFV, _ := vlGetGlobal(vl) // We just inserted something above, can't be empty - - if !haveOldGlobal { - return newFV, nil, removedFV, false, haveRemoved, true, nil - } - - globalChanged := true - if fvIsInvalid(oldFV) == fvIsInvalid(newFV) && protocol.VectorFromWire(oldFV.Version).Equal(protocol.VectorFromWire(newFV.Version)) { - globalChanged = false - } - - return newFV, oldFV, removedFV, true, haveRemoved, globalChanged, nil -} - -func vlInsert(vl *dbproto.VersionList, folder, device []byte, file protocol.FileInfo, t readOnlyTransaction) error { - var added bool - var err error - i := 0 - for ; i < len(vl.Versions); i++ { - // Insert our new version - added, err = vlCheckInsertAt(vl, i, folder, device, file, t) - if err != nil { - return err - } - if added { - break - } - } - if i == len(vl.Versions) { - // Append to the end - vl.Versions = append(vl.Versions, newFileVersion(device, file.FileVersion(), file.IsInvalid(), file.IsDeleted())) - } - return nil -} - -func vlInsertAt(vl *dbproto.VersionList, i int, v *dbproto.FileVersion) { - vl.Versions = append(vl.Versions, &dbproto.FileVersion{}) - copy(vl.Versions[i+1:], vl.Versions[i:]) - vl.Versions[i] = v -} - -// pop removes the given device from the VersionList and returns the FileVersion -// before removing the device, whether it was found/removed at all and whether -// the global changed in the process. -func vlPop(vl *dbproto.VersionList, device []byte) (*dbproto.FileVersion, bool, bool) { - invDevice, i, j, ok := vlFindDevice(vl, device) - if !ok { - return nil, false, false - } - globalPos := vlFindGlobal(vl) - - fv := vl.Versions[i] - if fvDeviceCount(fv) == 1 { - vlPopVersionAt(vl, i) - return fv, true, globalPos == i - } - - oldFV := fvCopy(fv) - if invDevice { - vl.Versions[i].InvalidDevices = popDeviceAt(vl.Versions[i].InvalidDevices, j) - return oldFV, true, false - } - vl.Versions[i].Devices = popDeviceAt(vl.Versions[i].Devices, j) - // If the last valid device of the previous global was removed above, - // the global changed. - return oldFV, true, len(vl.Versions[i].Devices) == 0 && globalPos == i -} - -// Get returns a FileVersion that contains the given device and whether it has -// been found at all. -func vlGet(vl *dbproto.VersionList, device []byte) (*dbproto.FileVersion, bool) { - _, i, _, ok := vlFindDevice(vl, device) - if !ok { - return &dbproto.FileVersion{}, false - } - return vl.Versions[i], true -} - -// GetGlobal returns the current global FileVersion. The returned FileVersion -// may be invalid, if all FileVersions are invalid. Returns false only if -// VersionList is empty. -func vlGetGlobal(vl *dbproto.VersionList) (*dbproto.FileVersion, bool) { - i := vlFindGlobal(vl) - if i == -1 { - return nil, false - } - return vl.Versions[i], true -} - -// findGlobal returns the first version that isn't invalid, or if all versions are -// invalid just the first version (i.e. 0) or -1, if there's no versions at all. -func vlFindGlobal(vl *dbproto.VersionList) int { - for i := range vl.Versions { - if !fvIsInvalid(vl.Versions[i]) { - return i - } - } - if len(vl.Versions) == 0 { - return -1 - } - return 0 -} - -// findDevice returns whether the device is in InvalidVersions or Versions and -// in InvalidDevices or Devices (true for invalid), the positions in the version -// and device slices and whether it has been found at all. -func vlFindDevice(vl *dbproto.VersionList, device []byte) (bool, int, int, bool) { - for i, v := range vl.Versions { - if j := deviceIndex(v.Devices, device); j != -1 { - return false, i, j, true - } - if j := deviceIndex(v.InvalidDevices, device); j != -1 { - return true, i, j, true - } - } - return false, -1, -1, false -} - -func vlPopVersionAt(vl *dbproto.VersionList, i int) { - vl.Versions = append(vl.Versions[:i], vl.Versions[i+1:]...) -} - -// checkInsertAt determines if the given device and associated file should be -// inserted into the FileVersion at position i or into a new FileVersion at -// position i. -func vlCheckInsertAt(vl *dbproto.VersionList, i int, folder, device []byte, file protocol.FileInfo, t readOnlyTransaction) (bool, error) { - fv := vl.Versions[i] - ordering := protocol.VectorFromWire(fv.Version).Compare(file.FileVersion()) - if ordering == protocol.Equal { - if !file.IsInvalid() { - fv.Devices = append(fv.Devices, device) - } else { - fv.InvalidDevices = append(fv.InvalidDevices, device) - } - return true, nil - } - existingDevice, _ := fvFirstDevice(fv) - insert, err := shouldInsertBefore(ordering, folder, existingDevice, fvIsInvalid(fv), file, t) - if err != nil { - return false, err - } - if insert { - vlInsertAt(vl, i, newFileVersion(device, file.FileVersion(), file.IsInvalid(), file.IsDeleted())) - return true, nil - } - return false, nil -} - -// shouldInsertBefore determines whether the file comes before an existing -// entry, given the version ordering (existing compared to new one), existing -// device and if the existing version is invalid. -func shouldInsertBefore(ordering protocol.Ordering, folder, existingDevice []byte, existingInvalid bool, file protocol.FileInfo, t readOnlyTransaction) (bool, error) { - switch ordering { - case protocol.Lesser: - // The version at this point in the list is lesser - // ("older") than us. We insert ourselves in front of it. - return true, nil - - case protocol.ConcurrentLesser, protocol.ConcurrentGreater: - // The version in conflict with us. - // Check if we can shortcut due to one being invalid. - if existingInvalid != file.IsInvalid() { - return existingInvalid, nil - } - // We must pull the actual file metadata to determine who wins. - // If we win, we insert ourselves in front of the loser here. - // (The "Lesser" and "Greater" in the condition above is just - // based on the device IDs in the version vector, which is not - // the only thing we use to determine the winner.) - of, ok, err := t.getFile(folder, existingDevice, []byte(file.FileName())) - if err != nil { - return false, err - } - // A surprise missing file entry here is counted as a win for us. - if !ok { - return true, nil - } - if file.WinsConflict(of) { - return true, nil - } - } - return false, nil -} - -func deviceIndex(devices [][]byte, device []byte) int { - for i, dev := range devices { - if bytes.Equal(device, dev) { - return i - } - } - return -1 -} - -func popDeviceAt(devices [][]byte, i int) [][]byte { - return append(devices[:i], devices[i+1:]...) -} - -func newFileVersion(device []byte, version protocol.Vector, invalid, deleted bool) *dbproto.FileVersion { - fv := &dbproto.FileVersion{ - Version: version.ToWire(), - Deleted: deleted, - } - if invalid { - fv.InvalidDevices = [][]byte{device} - } else { - fv.Devices = [][]byte{device} - } - return fv -} - -func fvFirstDevice(fv *dbproto.FileVersion) ([]byte, bool) { - if len(fv.Devices) != 0 { - return fv.Devices[0], true - } - if len(fv.InvalidDevices) != 0 { - return fv.InvalidDevices[0], true - } - return nil, false -} - -func fvIsInvalid(fv *dbproto.FileVersion) bool { - return fv == nil || len(fv.Devices) == 0 -} - -func fvDeviceCount(fv *dbproto.FileVersion) int { - return len(fv.Devices) + len(fv.InvalidDevices) -} - -func fvCopy(fv *dbproto.FileVersion) *dbproto.FileVersion { - return proto.Clone(fv).(*dbproto.FileVersion) -} diff --git a/lib/db/transactions.go b/lib/db/transactions.go deleted file mode 100644 index 9efeff21e..000000000 --- a/lib/db/transactions.go +++ /dev/null @@ -1,1008 +0,0 @@ -// Copyright (C) 2014 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package db - -import ( - "bytes" - "errors" - "fmt" - - "google.golang.org/protobuf/proto" - - "github.com/syncthing/syncthing/internal/gen/bep" - "github.com/syncthing/syncthing/internal/gen/dbproto" - "github.com/syncthing/syncthing/lib/db/backend" - "github.com/syncthing/syncthing/lib/events" - "github.com/syncthing/syncthing/lib/osutil" - "github.com/syncthing/syncthing/lib/protocol" - "github.com/syncthing/syncthing/lib/sliceutil" -) - -var ( - errEntryFromGlobalMissing = errors.New("device present in global list but missing as device/fileinfo entry") - errEmptyGlobal = errors.New("no versions in global list") - errEmptyFileVersion = errors.New("no devices in global file version") -) - -// A readOnlyTransaction represents a database snapshot. -type readOnlyTransaction struct { - backend.ReadTransaction - keyer keyer - evLogger events.Logger -} - -func (db *Lowlevel) newReadOnlyTransaction() (readOnlyTransaction, error) { - tran, err := db.NewReadTransaction() - if err != nil { - return readOnlyTransaction{}, err - } - return db.readOnlyTransactionFromBackendTransaction(tran), nil -} - -func (db *Lowlevel) readOnlyTransactionFromBackendTransaction(tran backend.ReadTransaction) readOnlyTransaction { - return readOnlyTransaction{ - ReadTransaction: tran, - keyer: db.keyer, - evLogger: db.evLogger, - } -} - -func (t readOnlyTransaction) close() { - t.Release() -} - -func (t readOnlyTransaction) getFile(folder, device, file []byte) (protocol.FileInfo, bool, error) { - key, err := t.keyer.GenerateDeviceFileKey(nil, folder, device, file) - if err != nil { - return protocol.FileInfo{}, false, err - } - return t.getFileByKey(key) -} - -func (t readOnlyTransaction) getFileByKey(key []byte) (protocol.FileInfo, bool, error) { - f, ok, err := t.getFileTrunc(key, false) - if err != nil || !ok { - return protocol.FileInfo{}, false, err - } - return f, true, nil -} - -func (t readOnlyTransaction) getFileTrunc(key []byte, trunc bool) (protocol.FileInfo, bool, error) { - bs, err := t.Get(key) - if backend.IsNotFound(err) { - return protocol.FileInfo{}, false, nil - } - if err != nil { - return protocol.FileInfo{}, false, err - } - f, err := t.unmarshalTrunc(bs, trunc) - if backend.IsNotFound(err) { - return protocol.FileInfo{}, false, nil - } - if err != nil { - return protocol.FileInfo{}, false, err - } - return f, true, nil -} - -func (t readOnlyTransaction) unmarshalTrunc(bs []byte, trunc bool) (protocol.FileInfo, error) { - if trunc { - var bfi dbproto.FileInfoTruncated - err := proto.Unmarshal(bs, &bfi) - if err != nil { - return protocol.FileInfo{}, err - } - if err := t.fillTruncated(&bfi); err != nil { - return protocol.FileInfo{}, err - } - return protocol.FileInfoFromDBTruncated(&bfi), nil - } - - var bfi bep.FileInfo - err := proto.Unmarshal(bs, &bfi) - if err != nil { - return protocol.FileInfo{}, err - } - if err := t.fillFileInfo(&bfi); err != nil { - return protocol.FileInfo{}, err - } - return protocol.FileInfoFromDB(&bfi), nil -} - -type blocksIndirectionError struct { - err error -} - -func (e *blocksIndirectionError) Error() string { - return fmt.Sprintf("filling Blocks: %v", e.err) -} - -func (e *blocksIndirectionError) Unwrap() error { - return e.err -} - -// fillFileInfo follows the (possible) indirection of blocks and version -// vector and fills it out. -func (t readOnlyTransaction) fillFileInfo(fi *bep.FileInfo) error { - var key []byte - - if len(fi.Blocks) == 0 && len(fi.BlocksHash) != 0 { - // The blocks list is indirected and we need to load it. - key = t.keyer.GenerateBlockListKey(key, fi.BlocksHash) - bs, err := t.Get(key) - if err != nil { - return &blocksIndirectionError{err} - } - var bl dbproto.BlockList - if err := proto.Unmarshal(bs, &bl); err != nil { - return err - } - fi.Blocks = bl.Blocks - } - - if len(fi.VersionHash) != 0 { - key = t.keyer.GenerateVersionKey(key, fi.VersionHash) - bs, err := t.Get(key) - if err != nil { - return fmt.Errorf("filling Version: %w", err) - } - var v bep.Vector - if err := proto.Unmarshal(bs, &v); err != nil { - return err - } - fi.Version = &v - } - - return nil -} - -// fillTruncated follows the (possible) indirection of version vector and -// fills it. -func (t readOnlyTransaction) fillTruncated(fi *dbproto.FileInfoTruncated) error { - var key []byte - - if len(fi.VersionHash) == 0 { - return nil - } - - key = t.keyer.GenerateVersionKey(key, fi.VersionHash) - bs, err := t.Get(key) - if err != nil { - return err - } - var v bep.Vector - if err := proto.Unmarshal(bs, &v); err != nil { - return err - } - fi.Version = &v - return nil -} - -func (t readOnlyTransaction) getGlobalVersions(keyBuf, folder, file []byte) (*dbproto.VersionList, error) { - var err error - keyBuf, err = t.keyer.GenerateGlobalVersionKey(keyBuf, folder, file) - if err != nil { - return nil, err - } - return t.getGlobalVersionsByKey(keyBuf) -} - -func (t readOnlyTransaction) getGlobalVersionsByKey(key []byte) (*dbproto.VersionList, error) { - bs, err := t.Get(key) - if err != nil { - return nil, err - } - - var vl dbproto.VersionList - if err := proto.Unmarshal(bs, &vl); err != nil { - return nil, err - } - - return &vl, nil -} - -func (t readOnlyTransaction) getGlobal(keyBuf, folder, file []byte, truncate bool) ([]byte, protocol.FileInfo, bool, error) { - vl, err := t.getGlobalVersions(keyBuf, folder, file) - if backend.IsNotFound(err) { - return keyBuf, protocol.FileInfo{}, false, nil - } else if err != nil { - return nil, protocol.FileInfo{}, false, err - } - keyBuf, fi, err := t.getGlobalFromVersionList(keyBuf, folder, file, truncate, vl) - return keyBuf, fi, true, err -} - -func (t readOnlyTransaction) getGlobalFromVersionList(keyBuf, folder, file []byte, truncate bool, vl *dbproto.VersionList) ([]byte, protocol.FileInfo, error) { - fv, ok := vlGetGlobal(vl) - if !ok { - return keyBuf, protocol.FileInfo{}, errEmptyGlobal - } - keyBuf, fi, err := t.getGlobalFromFileVersion(keyBuf, folder, file, truncate, fv) - return keyBuf, fi, err -} - -func (t readOnlyTransaction) getGlobalFromFileVersion(keyBuf, folder, file []byte, truncate bool, fv *dbproto.FileVersion) ([]byte, protocol.FileInfo, error) { - dev, ok := fvFirstDevice(fv) - if !ok { - return keyBuf, protocol.FileInfo{}, errEmptyFileVersion - } - keyBuf, err := t.keyer.GenerateDeviceFileKey(keyBuf, folder, dev, file) - if err != nil { - return keyBuf, protocol.FileInfo{}, err - } - fi, ok, err := t.getFileTrunc(keyBuf, truncate) - if err != nil { - return keyBuf, protocol.FileInfo{}, err - } - if !ok { - return keyBuf, protocol.FileInfo{}, errEntryFromGlobalMissing - } - return keyBuf, fi, nil -} - -func (t *readOnlyTransaction) withHave(folder, device, prefix []byte, truncate bool, fn Iterator) error { - if len(prefix) > 0 { - unslashedPrefix := prefix - if bytes.HasSuffix(prefix, []byte{'/'}) { - unslashedPrefix = unslashedPrefix[:len(unslashedPrefix)-1] - } else { - prefix = append(prefix, '/') - } - - key, err := t.keyer.GenerateDeviceFileKey(nil, folder, device, unslashedPrefix) - if err != nil { - return err - } - if f, ok, err := t.getFileTrunc(key, truncate); err != nil { - return err - } else if ok && !fn(f) { - return nil - } - } - - key, err := t.keyer.GenerateDeviceFileKey(nil, folder, device, prefix) - if err != nil { - return err - } - dbi, err := t.NewPrefixIterator(key) - if err != nil { - return err - } - defer dbi.Release() - - for dbi.Next() { - name := t.keyer.NameFromDeviceFileKey(dbi.Key()) - if len(prefix) > 0 && !bytes.HasPrefix(name, prefix) { - return nil - } - - f, err := t.unmarshalTrunc(dbi.Value(), truncate) - if err != nil { - l.Debugln("unmarshal error:", err) - continue - } - if !fn(f) { - return nil - } - } - return dbi.Error() -} - -func (t *readOnlyTransaction) withHaveSequence(folder []byte, startSeq int64, fn Iterator) error { - first, err := t.keyer.GenerateSequenceKey(nil, folder, startSeq) - if err != nil { - return err - } - last, err := t.keyer.GenerateSequenceKey(nil, folder, maxInt64) - if err != nil { - return err - } - dbi, err := t.NewRangeIterator(first, last) - if err != nil { - return err - } - defer dbi.Release() - - for dbi.Next() { - f, ok, err := t.getFileByKey(dbi.Value()) - if err != nil { - return err - } - if !ok { - l.Debugln("missing file for sequence number", t.keyer.SequenceFromSequenceKey(dbi.Key())) - continue - } - - if shouldDebug() { - if seq := t.keyer.SequenceFromSequenceKey(dbi.Key()); f.Sequence != seq { - l.Debugf("Sequence index corruption (folder %v, file %v): sequence %d != expected %d", string(folder), f.Name, f.Sequence, seq) - } - } - if !fn(f) { - return nil - } - } - return dbi.Error() -} - -func (t *readOnlyTransaction) withGlobal(folder, prefix []byte, truncate bool, fn Iterator) error { - if len(prefix) > 0 { - unslashedPrefix := prefix - if bytes.HasSuffix(prefix, []byte{'/'}) { - unslashedPrefix = unslashedPrefix[:len(unslashedPrefix)-1] - } else { - prefix = append(prefix, '/') - } - - if _, f, ok, err := t.getGlobal(nil, folder, unslashedPrefix, truncate); err != nil { - return err - } else if ok && !fn(f) { - return nil - } - } - - key, err := t.keyer.GenerateGlobalVersionKey(nil, folder, prefix) - if err != nil { - return err - } - dbi, err := t.NewPrefixIterator(key) - if err != nil { - return err - } - defer dbi.Release() - - var dk []byte - for dbi.Next() { - name := t.keyer.NameFromGlobalVersionKey(dbi.Key()) - if len(prefix) > 0 && !bytes.HasPrefix(name, prefix) { - return nil - } - - var vl dbproto.VersionList - if err := proto.Unmarshal(dbi.Value(), &vl); err != nil { - return err - } - - var f protocol.FileInfo - dk, f, err = t.getGlobalFromVersionList(dk, folder, name, truncate, &vl) - if err != nil { - return err - } - - if !fn(f) { - return nil - } - } - if err != nil { - return err - } - return dbi.Error() -} - -func (t *readOnlyTransaction) withBlocksHash(folder, hash []byte, iterator Iterator) error { - key, err := t.keyer.GenerateBlockListMapKey(nil, folder, hash, nil) - if err != nil { - return err - } - - iter, err := t.NewPrefixIterator(key) - if err != nil { - return err - } - defer iter.Release() - - for iter.Next() { - file := string(t.keyer.NameFromBlockListMapKey(iter.Key())) - f, ok, err := t.getFile(folder, protocol.LocalDeviceID[:], []byte(osutil.NormalizedFilename(file))) - if err != nil { - return err - } - if !ok { - continue - } - f.Name = osutil.NativeFilename(f.Name) - - if !bytes.Equal(f.BlocksHash, hash) { - msg := "Mismatching block map list hashes" - t.evLogger.Log(events.Failure, fmt.Sprintln(msg, "in withBlocksHash")) - l.Warnf("%v: got %x expected %x", msg, f.BlocksHash, hash) - continue - } - - if f.IsDeleted() || f.IsInvalid() || f.IsDirectory() || f.IsSymlink() { - msg := "Found something of unexpected type in block list map" - t.evLogger.Log(events.Failure, fmt.Sprintln(msg, "in withBlocksHash")) - l.Warnf("%v: %s", msg, f) - continue - } - - if !iterator(f) { - break - } - } - - return iter.Error() -} - -func (t *readOnlyTransaction) availability(folder, file []byte) ([]protocol.DeviceID, error) { - vl, err := t.getGlobalVersions(nil, folder, file) - if backend.IsNotFound(err) { - return nil, nil - } - if err != nil { - return nil, err - } - - fv, ok := vlGetGlobal(vl) - if !ok { - return nil, nil - } - devices := make([]protocol.DeviceID, len(fv.Devices)) - for i, dev := range fv.Devices { - n, err := protocol.DeviceIDFromBytes(dev) - if err != nil { - return nil, err - } - devices[i] = n - } - - return devices, nil -} - -func (t *readOnlyTransaction) withNeed(folder, device []byte, truncate bool, fn Iterator) error { - if bytes.Equal(device, protocol.LocalDeviceID[:]) { - return t.withNeedLocal(folder, truncate, fn) - } - return t.withNeedIteratingGlobal(folder, device, truncate, fn) -} - -func (t *readOnlyTransaction) withNeedIteratingGlobal(folder, device []byte, truncate bool, fn Iterator) error { - key, err := t.keyer.GenerateGlobalVersionKey(nil, folder, nil) - if err != nil { - return err - } - dbi, err := t.NewPrefixIterator(key.WithoutName()) - if err != nil { - return err - } - defer dbi.Release() - - var dk []byte - devID, err := protocol.DeviceIDFromBytes(device) - if err != nil { - return err - } - for dbi.Next() { - var vl dbproto.VersionList - if err := proto.Unmarshal(dbi.Value(), &vl); err != nil { - return err - } - - globalFV, ok := vlGetGlobal(&vl) - if !ok { - return errEmptyGlobal - } - haveFV, have := vlGet(&vl, device) - - if !Need(globalFV, have, protocol.VectorFromWire(haveFV.Version)) { - continue - } - - name := t.keyer.NameFromGlobalVersionKey(dbi.Key()) - var gf protocol.FileInfo - dk, gf, err = t.getGlobalFromFileVersion(dk, folder, name, truncate, globalFV) - if err != nil { - return err - } - - if shouldDebug() { - if globalDev, ok := fvFirstDevice(globalFV); ok { - globalID, _ := protocol.DeviceIDFromBytes(globalDev) - l.Debugf("need folder=%q device=%v name=%q have=%v invalid=%v haveV=%v haveDeleted=%v globalV=%v globalDeleted=%v globalDev=%v", folder, devID, name, have, fvIsInvalid(haveFV), haveFV.Version, haveFV.Deleted, gf.FileVersion(), globalFV.Deleted, globalID) - } - } - if !fn(gf) { - return dbi.Error() - } - } - return dbi.Error() -} - -func (t *readOnlyTransaction) withNeedLocal(folder []byte, truncate bool, fn Iterator) error { - key, err := t.keyer.GenerateNeedFileKey(nil, folder, nil) - if err != nil { - return err - } - dbi, err := t.NewPrefixIterator(key.WithoutName()) - if err != nil { - return err - } - defer dbi.Release() - - var keyBuf []byte - var f protocol.FileInfo - var ok bool - for dbi.Next() { - keyBuf, f, ok, err = t.getGlobal(keyBuf, folder, t.keyer.NameFromGlobalVersionKey(dbi.Key()), truncate) - if err != nil { - return err - } - if !ok { - continue - } - if !fn(f) { - return nil - } - } - return dbi.Error() -} - -// A readWriteTransaction is a readOnlyTransaction plus a batch for writes. -// The batch will be committed on close() or by checkFlush() if it exceeds the -// batch size. -type readWriteTransaction struct { - backend.WriteTransaction - readOnlyTransaction - indirectionTracker -} - -type indirectionTracker interface { - recordIndirectionHashesForFile(f *protocol.FileInfo) -} - -func (db *Lowlevel) newReadWriteTransaction(hooks ...backend.CommitHook) (readWriteTransaction, error) { - tran, err := db.NewWriteTransaction(hooks...) - if err != nil { - return readWriteTransaction{}, err - } - return readWriteTransaction{ - WriteTransaction: tran, - readOnlyTransaction: db.readOnlyTransactionFromBackendTransaction(tran), - indirectionTracker: db, - }, nil -} - -func (t readWriteTransaction) Commit() error { - // The readOnlyTransaction must close after commit, because they may be - // backed by the same actual lower level transaction. - defer t.readOnlyTransaction.close() - return t.WriteTransaction.Commit() -} - -func (t readWriteTransaction) close() { - t.readOnlyTransaction.close() - t.WriteTransaction.Release() -} - -// putFile stores a file in the database, taking care of indirected fields. -func (t readWriteTransaction) putFile(fkey []byte, fi protocol.FileInfo) error { - var bkey []byte - - // Always set the blocks hash when there are blocks. - if len(fi.Blocks) > 0 { - fi.BlocksHash = protocol.BlocksHash(fi.Blocks) - } else { - fi.BlocksHash = nil - } - - // Indirect the blocks if the block list is large enough. - if len(fi.Blocks) > blocksIndirectionCutoff { - bkey = t.keyer.GenerateBlockListKey(bkey, fi.BlocksHash) - if _, err := t.Get(bkey); backend.IsNotFound(err) { - // Marshal the block list and save it - blocks := sliceutil.Map(fi.Blocks, protocol.BlockInfo.ToWire) - blocksBs := mustMarshal(&dbproto.BlockList{Blocks: blocks}) - if err := t.Put(bkey, blocksBs); err != nil { - return err - } - } else if err != nil { - return err - } - fi.Blocks = nil - } - - // Indirect the version vector if it's large enough. - if len(fi.Version.Counters) > versionIndirectionCutoff { - fi.VersionHash = protocol.VectorHash(fi.Version) - bkey = t.keyer.GenerateVersionKey(bkey, fi.VersionHash) - if _, err := t.Get(bkey); backend.IsNotFound(err) { - // Marshal the version vector and save it - versionBs := mustMarshal(fi.Version.ToWire()) - if err := t.Put(bkey, versionBs); err != nil { - return err - } - } else if err != nil { - return err - } - fi.Version = protocol.Vector{} - } else { - fi.VersionHash = nil - } - - t.indirectionTracker.recordIndirectionHashesForFile(&fi) - - fiBs := mustMarshal(fi.ToWire(true)) - return t.Put(fkey, fiBs) -} - -// updateGlobal adds this device+version to the version list for the given -// file. If the device is already present in the list, the version is updated. -// If the file does not have an entry in the global list, it is created. -func (t readWriteTransaction) updateGlobal(gk, keyBuf, folder, device []byte, file protocol.FileInfo, meta *metadataTracker) ([]byte, error) { - deviceID, err := protocol.DeviceIDFromBytes(device) - if err != nil { - return nil, err - } - - l.Debugf("update global; folder=%q device=%v file=%q version=%v invalid=%v", folder, deviceID, file.Name, file.Version, file.IsInvalid()) - - fl, err := t.getGlobalVersionsByKey(gk) - if err != nil && !backend.IsNotFound(err) { - return nil, err - } - if fl == nil { - fl = &dbproto.VersionList{} - } - - globalFV, oldGlobalFV, removedFV, haveOldGlobal, haveRemoved, globalChanged, err := vlUpdate(fl, folder, device, file, t.readOnlyTransaction) - if err != nil { - return nil, err - } - - name := []byte(file.Name) - - l.Debugf(`new global for "%v" after update: %v`, file.Name, fl) - if err := t.Put(gk, mustMarshal(fl)); err != nil { - return nil, err - } - - // Only load those from db if actually needed - - var gotGlobal, gotOldGlobal bool - var global, oldGlobal protocol.FileInfo - - // Check the need of the device that was updated - // Must happen before updating global meta: If this is the first - // item from this device, it will be initialized with the global state. - - needBefore := haveOldGlobal && Need(oldGlobalFV, haveRemoved, protocol.VectorFromWire(removedFV.GetVersion())) - needNow := Need(globalFV, true, file.Version) - if needBefore { - if keyBuf, oldGlobal, err = t.getGlobalFromFileVersion(keyBuf, folder, name, true, oldGlobalFV); err != nil { - return nil, err - } - gotOldGlobal = true - meta.removeNeeded(deviceID, oldGlobal) - if !needNow && bytes.Equal(device, protocol.LocalDeviceID[:]) { - if keyBuf, err = t.updateLocalNeed(keyBuf, folder, name, false); err != nil { - return nil, err - } - } - } - if needNow { - keyBuf, global, err = t.getGlobalFromFileVersion(keyBuf, folder, name, true, globalFV) - if err != nil { - return nil, err - } - gotGlobal = true - meta.addNeeded(deviceID, global) - if !needBefore && bytes.Equal(device, protocol.LocalDeviceID[:]) { - if keyBuf, err = t.updateLocalNeed(keyBuf, folder, name, true); err != nil { - return nil, err - } - } - } - - // Update global size counter if necessary - - if !globalChanged { - // Neither the global state nor the needs of any devices, except - // the one updated, changed. - return keyBuf, nil - } - - // Remove the old global from the global size counter - if haveOldGlobal { - if !gotOldGlobal { - if keyBuf, oldGlobal, err = t.getGlobalFromFileVersion(keyBuf, folder, name, true, oldGlobalFV); err != nil { - return nil, err - } - } - // Remove the old global from the global size counter - meta.removeFile(protocol.GlobalDeviceID, oldGlobal) - } - - // Add the new global to the global size counter - if !gotGlobal { - if protocol.VectorFromWire(globalFV.Version).Equal(file.Version) { - // The inserted file is the global file - global = file - } else { - keyBuf, global, err = t.getGlobalFromFileVersion(keyBuf, folder, name, true, globalFV) - if err != nil { - return nil, err - } - } - } - meta.addFile(protocol.GlobalDeviceID, global) - - // check for local (if not already done before) - if !bytes.Equal(device, protocol.LocalDeviceID[:]) { - localFV, haveLocal := vlGet(fl, protocol.LocalDeviceID[:]) - localVersion := protocol.VectorFromWire(localFV.Version) - needBefore := haveOldGlobal && Need(oldGlobalFV, haveLocal, localVersion) - needNow := Need(globalFV, haveLocal, localVersion) - if needBefore { - meta.removeNeeded(protocol.LocalDeviceID, oldGlobal) - if !needNow { - if keyBuf, err = t.updateLocalNeed(keyBuf, folder, name, false); err != nil { - return nil, err - } - } - } - if needNow { - meta.addNeeded(protocol.LocalDeviceID, global) - if !needBefore { - if keyBuf, err = t.updateLocalNeed(keyBuf, folder, name, true); err != nil { - return nil, err - } - } - } - } - - for _, dev := range meta.devices() { - if bytes.Equal(dev[:], device) { - // Already handled above - continue - } - fv, have := vlGet(fl, dev[:]) - fvVersion := protocol.VectorFromWire(fv.Version) - if haveOldGlobal && Need(oldGlobalFV, have, fvVersion) { - meta.removeNeeded(dev, oldGlobal) - } - if Need(globalFV, have, fvVersion) { - meta.addNeeded(dev, global) - } - } - - return keyBuf, nil -} - -func (t readWriteTransaction) updateLocalNeed(keyBuf, folder, name []byte, add bool) ([]byte, error) { - var err error - keyBuf, err = t.keyer.GenerateNeedFileKey(keyBuf, folder, name) - if err != nil { - return nil, err - } - if add { - l.Debugf("local need insert; folder=%q, name=%q", folder, name) - err = t.Put(keyBuf, nil) - } else { - l.Debugf("local need delete; folder=%q, name=%q", folder, name) - err = t.Delete(keyBuf) - } - return keyBuf, err -} - -func Need(global *dbproto.FileVersion, haveLocal bool, localVersion protocol.Vector) bool { - // We never need an invalid file or a file without a valid version (just - // another way of expressing "invalid", really, until we fix that - // part...). - globalVersion := protocol.VectorFromWire(global.Version) - if fvIsInvalid(global) || globalVersion.IsEmpty() { - return false - } - // We don't need a deleted file if we don't have it. - if global.Deleted && !haveLocal { - return false - } - // We don't need the global file if we already have the same version. - if haveLocal && localVersion.GreaterEqual(globalVersion) { - return false - } - return true -} - -// removeFromGlobal removes the device from the global version list for the -// given file. If the version list is empty after this, the file entry is -// removed entirely. -func (t readWriteTransaction) removeFromGlobal(gk, keyBuf, folder, device, file []byte, meta *metadataTracker) ([]byte, error) { - deviceID, err := protocol.DeviceIDFromBytes(device) - if err != nil { - return nil, err - } - - l.Debugf("remove from global; folder=%q device=%v file=%q", folder, deviceID, file) - - fl, err := t.getGlobalVersionsByKey(gk) - if backend.IsNotFound(err) { - // We might be called to "remove" a global version that doesn't exist - // if the first update for the file is already marked invalid. - return keyBuf, nil - } else if err != nil { - return nil, err - } - - oldGlobalFV, haveOldGlobal := vlGetGlobal(fl) - oldGlobalFV = fvCopy(oldGlobalFV) - - if !haveOldGlobal { - // Shouldn't ever happen, but doesn't hurt to handle. - t.evLogger.Log(events.Failure, "encountered empty global while removing item") - return keyBuf, t.Delete(gk) - } - - removedFV, haveRemoved, globalChanged := vlPop(fl, device) - if !haveRemoved { - // There is no version for the given device - return keyBuf, nil - } - - var global protocol.FileInfo - var gotGlobal bool - - globalFV, haveGlobal := vlGetGlobal(fl) - // Add potential needs of the removed device - if haveGlobal && !fvIsInvalid(globalFV) && Need(globalFV, false, protocol.Vector{}) && !Need(oldGlobalFV, haveRemoved, protocol.VectorFromWire(removedFV.Version)) { - keyBuf, global, err = t.getGlobalFromVersionList(keyBuf, folder, file, true, fl) - if err != nil { - return nil, err - } - gotGlobal = true - meta.addNeeded(deviceID, global) - if bytes.Equal(protocol.LocalDeviceID[:], device) { - if keyBuf, err = t.updateLocalNeed(keyBuf, folder, file, true); err != nil { - return nil, err - } - } - } - - // Global hasn't changed, abort early - if !globalChanged { - l.Debugf("new global after remove: %v", fl) - if err := t.Put(gk, mustMarshal(fl)); err != nil { - return nil, err - } - return keyBuf, nil - } - - var oldGlobal protocol.FileInfo - keyBuf, oldGlobal, err = t.getGlobalFromFileVersion(keyBuf, folder, file, true, oldGlobalFV) - if err != nil { - return nil, err - } - meta.removeFile(protocol.GlobalDeviceID, oldGlobal) - - // Remove potential device needs - shouldRemoveNeed := func(dev protocol.DeviceID) bool { - fv, have := vlGet(fl, dev[:]) - fvVersion := protocol.VectorFromWire(fv.Version) - if !Need(oldGlobalFV, have, fvVersion) { - return false // Didn't need it before - } - return !haveGlobal || !Need(globalFV, have, fvVersion) - } - if shouldRemoveNeed(protocol.LocalDeviceID) { - meta.removeNeeded(protocol.LocalDeviceID, oldGlobal) - if keyBuf, err = t.updateLocalNeed(keyBuf, folder, file, false); err != nil { - return nil, err - } - } - for _, dev := range meta.devices() { - if bytes.Equal(dev[:], device) { // Was the previous global - continue - } - if shouldRemoveNeed(dev) { - meta.removeNeeded(dev, oldGlobal) - } - } - - // Nothing left, i.e. nothing to add to the global counter below. - if len(fl.Versions) == 0 { - if err := t.Delete(gk); err != nil { - return nil, err - } - return keyBuf, nil - } - - // Add to global - if !gotGlobal { - keyBuf, global, err = t.getGlobalFromVersionList(keyBuf, folder, file, true, fl) - if err != nil { - return nil, err - } - } - meta.addFile(protocol.GlobalDeviceID, global) - - l.Debugf(`new global for "%s" after remove: %v`, file, fl) - if err := t.Put(gk, mustMarshal(fl)); err != nil { - return nil, err - } - - return keyBuf, nil -} - -func (t readWriteTransaction) deleteKeyPrefix(prefix []byte) error { - return t.deleteKeyPrefixMatching(prefix, func([]byte) bool { return true }) -} - -func (t readWriteTransaction) deleteKeyPrefixMatching(prefix []byte, match func(key []byte) bool) error { - dbi, err := t.NewPrefixIterator(prefix) - if err != nil { - return err - } - defer dbi.Release() - for dbi.Next() { - if !match(dbi.Key()) { - continue - } - if err := t.Delete(dbi.Key()); err != nil { - return err - } - } - return dbi.Error() -} - -func (t *readWriteTransaction) withAllFolderTruncated(folder []byte, fn func(device []byte, f protocol.FileInfo) bool) error { - key, err := t.keyer.GenerateDeviceFileKey(nil, folder, nil, nil) - if err != nil { - return err - } - dbi, err := t.NewPrefixIterator(key.WithoutNameAndDevice()) - if err != nil { - return err - } - defer dbi.Release() - - var gk, keyBuf []byte - for dbi.Next() { - device, ok := t.keyer.DeviceFromDeviceFileKey(dbi.Key()) - if !ok { - // Not having the device in the index is bad. Clear it. - if err := t.Delete(dbi.Key()); err != nil { - return err - } - continue - } - - f, err := t.unmarshalTrunc(dbi.Value(), true) - if err != nil { - return err - } - - switch f.Name { - case "", ".", "..", "/": // A few obviously invalid filenames - l.Infof("Dropping invalid filename %q from database", f.Name) - name := []byte(f.Name) - gk, err = t.keyer.GenerateGlobalVersionKey(gk, folder, name) - if err != nil { - return err - } - keyBuf, err = t.removeFromGlobal(gk, keyBuf, folder, device, name, nil) - if err != nil { - return err - } - if err := t.Delete(dbi.Key()); err != nil { - return err - } - continue - } - - if !fn(device, f) { - return nil - } - } - return dbi.Error() -} - -func mustMarshal(f proto.Message) []byte { - bs, err := proto.Marshal(f) - if err != nil { - panic(err) - } - return bs -} diff --git a/lib/db/util_test.go b/lib/db/util_test.go deleted file mode 100644 index cc7d7d6ce..000000000 --- a/lib/db/util_test.go +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright (C) 2018 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package db - -import ( - "encoding/json" - "errors" - "io" - "os" - "testing" - - "github.com/syncthing/syncthing/lib/db/backend" - "github.com/syncthing/syncthing/lib/events" - "github.com/syncthing/syncthing/lib/protocol" -) - -// writeJSONS serializes the database to a JSON stream that can be checked -// in to the repo and used for tests. -func writeJSONS(w io.Writer, db backend.Backend) { - it, err := db.NewPrefixIterator(nil) - if err != nil { - panic(err) - } - defer it.Release() - enc := json.NewEncoder(w) - for it.Next() { - err := enc.Encode(map[string][]byte{ - "k": it.Key(), - "v": it.Value(), - }) - if err != nil { - panic(err) - } - } -} - -// we know this function isn't generally used, nonetheless we want it in -// here and the linter to not complain. -var _ = writeJSONS - -// openJSONS reads a JSON stream file into a backend DB -func openJSONS(file string) (backend.Backend, error) { - fd, err := os.Open(file) - if err != nil { - return nil, err - } - dec := json.NewDecoder(fd) - - db := backend.OpenMemory() - - for { - var row map[string][]byte - - err := dec.Decode(&row) - if err == io.EOF { - break - } else if err != nil { - return nil, err - } - - if err := db.Put(row["k"], row["v"]); err != nil { - return nil, err - } - } - - return db, nil -} - -func newLowlevel(t testing.TB, backend backend.Backend) *Lowlevel { - t.Helper() - ll, err := NewLowlevel(backend, events.NoopLogger) - if err != nil { - t.Fatal(err) - } - return ll -} - -func newLowlevelMemory(t testing.TB) *Lowlevel { - return newLowlevel(t, backend.OpenMemory()) -} - -func newFileSet(t testing.TB, folder string, db *Lowlevel) *FileSet { - t.Helper() - fset, err := NewFileSet(folder, db) - if err != nil { - t.Fatal(err) - } - return fset -} - -func snapshot(t testing.TB, fset *FileSet) *Snapshot { - t.Helper() - snap, err := fset.Snapshot() - if err != nil { - t.Fatal(err) - } - return snap -} - -// The following commented tests were used to generate jsons files to stdout for -// future tests and are kept here for reference (reuse). - -// TestGenerateIgnoredFilesDB generates a database with files with invalid flags, -// local and remote, in the format used in 0.14.48. -// func TestGenerateIgnoredFilesDB(t *testing.T) { -// db := OpenMemory() -// fs := newFileSet(t, "test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), db) -// fs.Update(protocol.LocalDeviceID, []protocol.FileInfo{ -// { // invalid (ignored) file -// Name: "foo", -// Type: protocol.FileInfoTypeFile, -// Invalid: true, -// Version: protocol.Vector{Counters: []protocol.Counter{{ID: 1, Value: 1000}}}, -// }, -// { // regular file -// Name: "bar", -// Type: protocol.FileInfoTypeFile, -// Version: protocol.Vector{Counters: []protocol.Counter{{ID: 1, Value: 1001}}}, -// }, -// }) -// fs.Update(protocol.DeviceID{42}, []protocol.FileInfo{ -// { // invalid file -// Name: "baz", -// Type: protocol.FileInfoTypeFile, -// Invalid: true, -// Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 1000}}}, -// }, -// { // regular file -// Name: "quux", -// Type: protocol.FileInfoTypeFile, -// Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 1002}}}, -// }, -// }) -// writeJSONS(os.Stdout, db.DB) -// } - -// TestGenerateUpdate0to3DB generates a database with files with invalid flags, prefixed -// by a slash and other files to test database migration from version 0 to 3, in the -// format used in 0.14.45. -// func TestGenerateUpdate0to3DB(t *testing.T) { -// db := OpenMemory() -// fs := newFileSet(t, update0to3Folder, fs.NewFilesystem(fs.FilesystemTypeBasic, "."), db) -// for devID, files := range haveUpdate0to3 { -// fs.Update(devID, files) -// } -// writeJSONS(os.Stdout, db.DB) -// } - -// func TestGenerateUpdateTo10(t *testing.T) { -// db := newLowlevelMemory(t) -// defer db.Close() - -// if err := UpdateSchema(db); err != nil { -// t.Fatal(err) -// } - -// fs := newFileSet(t, "test", fs.NewFilesystem(fs.FilesystemTypeFake, ""), db) - -// files := []protocol.FileInfo{ -// {Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Deleted: true, Sequence: 1}, -// {Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(2), Sequence: 2}, -// {Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Deleted: true, Sequence: 3}, -// } -// fs.Update(protocol.LocalDeviceID, files) -// files[1].Version = files[1].Version.Update(remoteDevice0.Short()) -// files[1].Deleted = true -// files[2].Version = files[2].Version.Update(remoteDevice0.Short()) -// files[2].Blocks = genBlocks(1) -// files[2].Deleted = false -// fs.Update(remoteDevice0, files) - -// fd, err := os.Create("./testdata/v1.4.0-updateTo10.json") -// if err != nil { -// panic(err) -// } -// defer fd.Close() -// writeJSONS(fd, db) -// } - -func TestFileInfoBatchError(t *testing.T) { - // Verify behaviour of the flush function returning an error. - - var errReturn error - var called int - b := NewFileInfoBatch(func([]protocol.FileInfo) error { - called += 1 - return errReturn - }) - - // Flush should work when the flush function error is nil - b.Append(protocol.FileInfo{Name: "test"}) - if err := b.Flush(); err != nil { - t.Fatalf("expected nil, got %v", err) - } - if called != 1 { - t.Fatalf("expected 1, got %d", called) - } - - // Flush should fail with an error retur - errReturn = errors.New("problem") - b.Append(protocol.FileInfo{Name: "test"}) - if err := b.Flush(); err != errReturn { - t.Fatalf("expected %v, got %v", errReturn, err) - } - if called != 2 { - t.Fatalf("expected 2, got %d", called) - } - - // Flush function should not be called again when it's already errored, - // same error should be returned by Flush() - if err := b.Flush(); err != errReturn { - t.Fatalf("expected %v, got %v", errReturn, err) - } - if called != 2 { - t.Fatalf("expected 2, got %d", called) - } - - // Reset should clear the error (and the file list) - errReturn = nil - b.Reset() - b.Append(protocol.FileInfo{Name: "test"}) - if err := b.Flush(); err != nil { - t.Fatalf("expected nil, got %v", err) - } - if called != 3 { - t.Fatalf("expected 3, got %d", called) - } -} diff --git a/lib/fs/basicfs.go b/lib/fs/basicfs.go index 098769870..4ed60d10e 100644 --- a/lib/fs/basicfs.go +++ b/lib/fs/basicfs.go @@ -379,12 +379,12 @@ func longFilenameSupport(path string) string { return path } -type ErrWatchEventOutsideRoot struct{ msg string } +type WatchEventOutsideRootError struct{ msg string } -func (e *ErrWatchEventOutsideRoot) Error() string { +func (e *WatchEventOutsideRootError) Error() string { return e.msg } -func (f *BasicFilesystem) newErrWatchEventOutsideRoot(absPath string, roots []string) *ErrWatchEventOutsideRoot { - return &ErrWatchEventOutsideRoot{fmt.Sprintf("Watching for changes encountered an event outside of the filesystem root: f.root==%v, roots==%v, path==%v. This should never happen, please report this message to forum.syncthing.net.", f.root, roots, absPath)} +func (f *BasicFilesystem) newErrWatchEventOutsideRoot(absPath string, roots []string) *WatchEventOutsideRootError { + return &WatchEventOutsideRootError{fmt.Sprintf("Watching for changes encountered an event outside of the filesystem root: f.root==%v, roots==%v, path==%v. This should never happen, please report this message to forum.syncthing.net.", f.root, roots, absPath)} } diff --git a/lib/fs/basicfs_fileinfo_bsdish.go b/lib/fs/basicfs_fileinfo_bsdish.go index b8e60f329..10061ebfb 100644 --- a/lib/fs/basicfs_fileinfo_bsdish.go +++ b/lib/fs/basicfs_fileinfo_bsdish.go @@ -15,7 +15,7 @@ import ( ) func (fi basicFileInfo) InodeChangeTime() time.Time { - if sys, ok := fi.FileInfo.Sys().(*syscall.Stat_t); ok { + if sys, ok := fi.Sys().(*syscall.Stat_t); ok { return time.Unix(0, sys.Ctimespec.Nano()) } return time.Time{} diff --git a/lib/fs/basicfs_unix.go b/lib/fs/basicfs_unix.go index e1b3967eb..1a54b2652 100644 --- a/lib/fs/basicfs_unix.go +++ b/lib/fs/basicfs_unix.go @@ -86,7 +86,7 @@ func (f *BasicFilesystem) Remove(name string) error { // unrooted) or an error if the given path is not a subpath and handles the // special case when the given path is the folder root without a trailing // pathseparator. -func (f *BasicFilesystem) unrootedChecked(absPath string, roots []string) (string, *ErrWatchEventOutsideRoot) { +func (f *BasicFilesystem) unrootedChecked(absPath string, roots []string) (string, *WatchEventOutsideRootError) { for _, root := range roots { // Make sure the root ends with precisely one path separator, to // ease prefix comparisons. diff --git a/lib/fs/basicfs_xattr_unix.go b/lib/fs/basicfs_xattr_unix.go index f43445c87..f32929708 100644 --- a/lib/fs/basicfs_xattr_unix.go +++ b/lib/fs/basicfs_xattr_unix.go @@ -69,7 +69,7 @@ var xattrBufPool = sync.Pool{ } func getXattr(path, name string) ([]byte, error) { - buf := xattrBufPool.Get().([]byte) + buf := xattrBufPool.Get().([]byte) //nolint:forcetypeassert defer func() { // Put the buffer back in the pool, or not if we're not supposed to // (we returned it to the caller). diff --git a/lib/fs/casefs.go b/lib/fs/casefs.go index d88ccc0a4..bf8ecb63b 100644 --- a/lib/fs/casefs.go +++ b/lib/fs/casefs.go @@ -24,16 +24,16 @@ const ( caseCacheItemLimit = 4 << 10 ) -type ErrCaseConflict struct { +type CaseConflictError struct { Given, Real string } -func (e *ErrCaseConflict) Error() string { +func (e *CaseConflictError) Error() string { return fmt.Sprintf(`remote "%v" uses different upper or lowercase characters than local "%v"; change the casing on either side to match the other`, e.Given, e.Real) } func IsErrCaseConflict(err error) bool { - e := &ErrCaseConflict{} + e := &CaseConflictError{} return errors.As(err, &e) } @@ -239,7 +239,7 @@ func (f *caseFilesystem) Rename(oldpath, newpath string) error { } if err := f.checkCase(newpath); err != nil { // Case-only rename is ok - e := &ErrCaseConflict{} + e := &CaseConflictError{} if !errors.As(err, &e) || e.Real != oldpath { return err } @@ -389,7 +389,7 @@ func (f *caseFilesystem) checkCaseExisting(name string) error { // comparing, as we don't want to treat a normalization difference as a // case conflict. if norm.NFC.String(realName) != norm.NFC.String(name) { - return &ErrCaseConflict{name, realName} + return &CaseConflictError{name, realName} } return nil } diff --git a/lib/fs/fakefs.go b/lib/fs/fakefs.go index af508f18c..3c056eab6 100644 --- a/lib/fs/fakefs.go +++ b/lib/fs/fakefs.go @@ -147,29 +147,29 @@ func newFakeFilesystem(rootURI string, _ ...Option) *fakeFS { // *look* like file I/O, but they are not. Do not worry that they // might fail. - rng := rand.New(rand.NewSource(int64(seed))) + rng := rand.New(rand.NewSource(int64(seed))) //nolint:gosec var createdFiles int var writtenData int64 for (files == 0 || createdFiles < files) && (maxsize == 0 || writtenData>>20 < int64(maxsize)) { dir := filepath.Join(fmt.Sprintf("%02x", rng.Intn(255)), fmt.Sprintf("%02x", rng.Intn(255))) file := fmt.Sprintf("%016x", rng.Int63()) - fs.MkdirAll(dir, 0o755) + _ = fs.MkdirAll(dir, 0o755) fd, _ := fs.Create(filepath.Join(dir, file)) createdFiles++ fsize := int64(sizeavg/2 + rng.Intn(sizeavg)) - fd.Truncate(fsize) + _ = fd.Truncate(fsize) writtenData += fsize ftime := time.Unix(1000000000+rng.Int63n(10*365*86400), 0) - fs.Chtimes(filepath.Join(dir, file), ftime, ftime) + _ = fs.Chtimes(filepath.Join(dir, file), ftime, ftime) } } if !nostfolder { // Also create a default folder marker for good measure - fs.Mkdir(".stfolder", 0o700) + _ = fs.Mkdir(".stfolder", 0o700) } // We only set the latency after doing the operations required to create @@ -284,9 +284,10 @@ func (fs *fakeFS) create(name string) (*fakeEntry, error) { time.Sleep(fs.latency) if entry := fs.entryForName(name); entry != nil { - if entry.entryType == fakeEntryTypeDir { + switch entry.entryType { + case fakeEntryTypeDir: return nil, os.ErrExist - } else if entry.entryType == fakeEntryTypeSymlink { + case fakeEntryTypeSymlink: return nil, errors.New("following symlink not supported") } entry.size = 0 @@ -731,6 +732,7 @@ func (fs *fakeFS) resetCounters() { } func (fs *fakeFS) reportMetricsPerOp(b *testing.B) { + b.Helper() fs.reportMetricsPer(b, 1, "op") } @@ -829,7 +831,7 @@ func (f *fakeFile) readShortAt(p []byte, offs int64) (int, error) { if f.seed == 0 { hf := fnv.New64() hf.Write([]byte(f.name)) - f.seed = int64(hf.Sum64()) + f.seed = int64(hf.Sum64()) //nolint:gosec } // Check whether the read is a continuation of an RNG we already have or @@ -839,14 +841,14 @@ func (f *fakeFile) readShortAt(p []byte, offs int64) (int, error) { nextBlockOffs := (seedNo + 1) << randomBlockShift if f.rng == nil || f.offset != offs || seedNo != f.seedOffs { // This is not a straight read continuing from a previous one - f.rng = rand.New(rand.NewSource(f.seed + seedNo)) + f.rng = rand.New(rand.NewSource(f.seed + seedNo)) //nolint:gosec // If the read is not at the start of the block, discard data // accordingly. diff := offs - minOffs if diff > 0 { lr := io.LimitReader(f.rng, diff) - io.Copy(io.Discard, lr) + _, _ = io.Copy(io.Discard, lr) } f.offset = offs diff --git a/lib/fs/filesystem.go b/lib/fs/filesystem.go index fa34fe9fd..368ba1ca5 100644 --- a/lib/fs/filesystem.go +++ b/lib/fs/filesystem.go @@ -180,7 +180,7 @@ const ( // SkipDir is used as a return value from WalkFuncs to indicate that // the directory named in the call is to be skipped. It is not returned // as an error by any function. -var SkipDir = filepath.SkipDir +var SkipDir = filepath.SkipDir //nolint:errname func IsExist(err error) bool { return errors.Is(err, ErrExist) @@ -259,15 +259,16 @@ func NewFilesystem(fsType FilesystemType, uri string, opts ...Option) Filesystem // attributed to the calling function. layersAboveWalkFilesystem++ } - if l.ShouldDebug("walkfs") { + switch { + case l.ShouldDebug("walkfs"): // A walkFilesystem is not a layer to skip, it embeds the underlying // filesystem, passing calls directly trough. Except for calls made // during walking, however those are truly originating in the walk // filesystem. fs = NewWalkFilesystem(newLogFilesystem(fs, layersAboveWalkFilesystem)) - } else if l.ShouldDebug("fs") { + case l.ShouldDebug("fs"): fs = newLogFilesystem(NewWalkFilesystem(fs), layersAboveWalkFilesystem) - } else { + default: fs = NewWalkFilesystem(fs) } diff --git a/lib/fs/filesystem_copy_range_standard.go b/lib/fs/filesystem_copy_range_standard.go index cd49fafe0..f59e0fd64 100644 --- a/lib/fs/filesystem_copy_range_standard.go +++ b/lib/fs/filesystem_copy_range_standard.go @@ -7,6 +7,7 @@ package fs import ( + "errors" "io" ) @@ -28,7 +29,7 @@ func copyRangeStandard(src, dst File, srcOffset, dstOffset, size int64) error { } n, err := src.ReadAt(buf, srcOffset) if err != nil { - if err == io.EOF { + if errors.Is(err, io.EOF) { return io.ErrUnexpectedEOF } return err diff --git a/lib/fs/filesystem_test.go b/lib/fs/filesystem_test.go index b0b239a5b..08e45ff50 100644 --- a/lib/fs/filesystem_test.go +++ b/lib/fs/filesystem_test.go @@ -189,7 +189,7 @@ func TestRepro9677MissingMtimeFS(t *testing.T) { testTime := time.Unix(1723491493, 123456789) // Create a file with an mtime FS entry - firstFS := NewFilesystem(FilesystemTypeFake, fmt.Sprintf("%v?insens=true&timeprecisionsecond=true", t.Name()), &OptionDetectCaseConflicts{}, NewMtimeOption(mtimeDB)) + firstFS := NewFilesystem(FilesystemTypeFake, fmt.Sprintf("%v?insens=true&timeprecisionsecond=true", t.Name()), &OptionDetectCaseConflicts{}, NewMtimeOption(mtimeDB, "")) // Create a file, set its mtime and check that we get the expected mtime when stat-ing. file, err := firstFS.Create(name) @@ -231,6 +231,6 @@ func TestRepro9677MissingMtimeFS(t *testing.T) { // be without mtime, even if requested: NewFilesystem(FilesystemTypeFake, fmt.Sprintf("%v?insens=true&timeprecisionsecond=true", t.Name()), &OptionDetectCaseConflicts{}) - newFS := NewFilesystem(FilesystemTypeFake, fmt.Sprintf("%v?insens=true&timeprecisionsecond=true", t.Name()), &OptionDetectCaseConflicts{}, NewMtimeOption(mtimeDB)) + newFS := NewFilesystem(FilesystemTypeFake, fmt.Sprintf("%v?insens=true&timeprecisionsecond=true", t.Name()), &OptionDetectCaseConflicts{}, NewMtimeOption(mtimeDB, "")) checkMtime(newFS) } diff --git a/lib/fs/mtimefs.go b/lib/fs/mtimefs.go index 5af16cb2a..523652d77 100644 --- a/lib/fs/mtimefs.go +++ b/lib/fs/mtimefs.go @@ -7,21 +7,21 @@ package fs import ( - "errors" "time" ) // The database is where we store the virtual mtimes type database interface { - Bytes(key string) (data []byte, ok bool, err error) - PutBytes(key string, data []byte) error - Delete(key string) error + GetMtime(folder, name string) (ondisk, virtual time.Time) + PutMtime(folder, name string, ondisk, virtual time.Time) error + DeleteMtime(folder, name string) error } type mtimeFS struct { Filesystem chtimes func(string, time.Time, time.Time) error db database + folderID string caseInsensitive bool } @@ -34,16 +34,18 @@ func WithCaseInsensitivity(v bool) MtimeFSOption { } type optionMtime struct { - db database - options []MtimeFSOption + db database + folderID string + options []MtimeFSOption } // NewMtimeOption makes any filesystem provide nanosecond mtime precision, // regardless of what shenanigans the underlying filesystem gets up to. -func NewMtimeOption(db database, options ...MtimeFSOption) Option { +func NewMtimeOption(db database, folderID string, options ...MtimeFSOption) Option { return &optionMtime{ - db: db, - options: options, + db: db, + folderID: folderID, + options: options, } } @@ -52,6 +54,7 @@ func (o *optionMtime) apply(fs Filesystem) Filesystem { Filesystem: fs, chtimes: fs.Chtimes, // for mocking it out in the tests db: o.db, + folderID: o.folderID, } for _, opt := range o.options { opt(f) @@ -65,7 +68,7 @@ func (*optionMtime) String() string { func (f *mtimeFS) Chtimes(name string, atime, mtime time.Time) error { // Do a normal Chtimes call, don't care if it succeeds or not. - f.chtimes(name, atime, mtime) + _ = f.chtimes(name, atime, mtime) // Stat the file to see what happened. Here we *do* return an error, // because it might be "does not exist" or similar. @@ -84,14 +87,11 @@ func (f *mtimeFS) Stat(name string) (FileInfo, error) { return nil, err } - mtimeMapping, err := f.load(name) - if err != nil { - return nil, err - } - if mtimeMapping.Real.Equal(info.ModTime()) { + ondisk, virtual := f.load(name) + if ondisk.Equal(info.ModTime()) { info = mtimeFileInfo{ FileInfo: info, - mtime: mtimeMapping.Virtual, + mtime: virtual, } } @@ -104,14 +104,11 @@ func (f *mtimeFS) Lstat(name string) (FileInfo, error) { return nil, err } - mtimeMapping, err := f.load(name) - if err != nil { - return nil, err - } - if mtimeMapping.Real.Equal(info.ModTime()) { + ondisk, virtual := f.load(name) + if ondisk.Equal(info.ModTime()) { info = mtimeFileInfo{ FileInfo: info, - mtime: mtimeMapping.Virtual, + mtime: virtual, } } @@ -146,43 +143,27 @@ func (f *mtimeFS) underlying() (Filesystem, bool) { return f.Filesystem, true } -func (f *mtimeFS) save(name string, real, virtual time.Time) { +func (f *mtimeFS) save(name string, ondisk, virtual time.Time) { if f.caseInsensitive { name = UnicodeLowercaseNormalized(name) } - if real.Equal(virtual) { + if ondisk.Equal(virtual) { // If the virtual time and the real on disk time are equal we don't // need to store anything. - f.db.Delete(name) + _ = f.db.DeleteMtime(f.folderID, name) return } - mtime := MtimeMapping{ - Real: real, - Virtual: virtual, - } - bs, _ := mtime.Marshal() // Can't fail - f.db.PutBytes(name, bs) + _ = f.db.PutMtime(f.folderID, name, ondisk, virtual) } -func (f *mtimeFS) load(name string) (MtimeMapping, error) { +func (f *mtimeFS) load(name string) (ondisk, virtual time.Time) { if f.caseInsensitive { name = UnicodeLowercaseNormalized(name) } - data, exists, err := f.db.Bytes(name) - if err != nil { - return MtimeMapping{}, err - } else if !exists { - return MtimeMapping{}, nil - } - - var mtime MtimeMapping - if err := mtime.Unmarshal(data); err != nil { - return MtimeMapping{}, err - } - return mtime, nil + return f.db.GetMtime(f.folderID, name) } // The mtimeFileInfo is an os.FileInfo that lies about the ModTime(). @@ -207,14 +188,11 @@ func (f mtimeFile) Stat() (FileInfo, error) { return nil, err } - mtimeMapping, err := f.fs.load(f.Name()) - if err != nil { - return nil, err - } - if mtimeMapping.Real.Equal(info.ModTime()) { + ondisk, virtual := f.fs.load(f.Name()) + if ondisk.Equal(info.ModTime()) { info = mtimeFileInfo{ FileInfo: info, - mtime: mtimeMapping.Virtual, + mtime: virtual, } } @@ -226,34 +204,14 @@ func (f mtimeFile) unwrap() File { return f.File } -// MtimeMapping represents the mapping as stored in the database -type MtimeMapping struct { - // "Real" is the on disk timestamp - Real time.Time `json:"real"` - // "Virtual" is what want the timestamp to be - Virtual time.Time `json:"virtual"` -} - -func (t *MtimeMapping) Marshal() ([]byte, error) { - bs0, _ := t.Real.MarshalBinary() - bs1, _ := t.Virtual.MarshalBinary() - return append(bs0, bs1...), nil -} - -func (t *MtimeMapping) Unmarshal(bs []byte) error { - if err := t.Real.UnmarshalBinary(bs[:len(bs)/2]); err != nil { - return err - } - if err := t.Virtual.UnmarshalBinary(bs[len(bs)/2:]); err != nil { - return err - } - return nil -} - -func GetMtimeMapping(fs Filesystem, file string) (MtimeMapping, error) { - mtimeFs, ok := unwrapFilesystem[*mtimeFS](fs) +func GetMtimeMapping(fs Filesystem, file string) (ondisk, virtual time.Time) { + fs, ok := unwrapFilesystem[*mtimeFS](fs) if !ok { - return MtimeMapping{}, errors.New("failed to unwrap") + return time.Time{}, time.Time{} + } + mtimeFs, ok := fs.(*mtimeFS) + if !ok { + return time.Time{}, time.Time{} } return mtimeFs.load(file) } diff --git a/lib/fs/mtimefs_test.go b/lib/fs/mtimefs_test.go index 4e0687666..60ee52382 100644 --- a/lib/fs/mtimefs_test.go +++ b/lib/fs/mtimefs_test.go @@ -226,20 +226,20 @@ func TestMtimeFSInsensitive(t *testing.T) { // The mapStore is a simple database -type mapStore map[string][]byte +type mapStore map[string][2]time.Time -func (s mapStore) PutBytes(key string, data []byte) error { - s[key] = data +func (s mapStore) PutMtime(_, name string, real, virtual time.Time) error { + s[name] = [2]time.Time{real, virtual} return nil } -func (s mapStore) Bytes(key string) (data []byte, ok bool, err error) { - data, ok = s[key] - return +func (s mapStore) GetMtime(_, name string) (real, virtual time.Time) { + v := s[name] + return v[0], v[1] } -func (s mapStore) Delete(key string) error { - delete(s, key) +func (s mapStore) DeleteMtime(_, name string) error { + delete(s, name) return nil } @@ -260,7 +260,7 @@ func newMtimeFS(path string, db database, options ...MtimeFSOption) *mtimeFS { } func newMtimeFSWithWalk(path string, db database, options ...MtimeFSOption) (*mtimeFS, *walkFilesystem) { - fs := NewFilesystem(FilesystemTypeBasic, path, NewMtimeOption(db, options...)) + fs := NewFilesystem(FilesystemTypeBasic, path, NewMtimeOption(db, "", options...)) wfs, _ := unwrapFilesystem[*walkFilesystem](fs) mfs, _ := unwrapFilesystem[*mtimeFS](fs) return mfs, wfs diff --git a/lib/fs/types.go b/lib/fs/types.go index 7dc86c9fb..224e24389 100644 --- a/lib/fs/types.go +++ b/lib/fs/types.go @@ -26,8 +26,10 @@ type Option interface { type FilesystemFactory func(string, ...Option) (Filesystem, error) // For each registered file system type, a function to construct a file system. -var filesystemFactories map[FilesystemType]FilesystemFactory = make(map[FilesystemType]FilesystemFactory) -var filesystemFactoriesMutex sync.Mutex = sync.Mutex{} +var ( + filesystemFactories map[FilesystemType]FilesystemFactory = make(map[FilesystemType]FilesystemFactory) + filesystemFactoriesMutex sync.Mutex = sync.Mutex{} +) // Register a function to be called when a filesystem is to be constructed with // the specified fsType. The function will receive the URI for the file system as well diff --git a/lib/fs/util.go b/lib/fs/util.go index 41ee39300..94a0a5416 100644 --- a/lib/fs/util.go +++ b/lib/fs/util.go @@ -192,7 +192,7 @@ func CommonPrefix(first, second string) string { } common := make([]string, 0, count) - for i := 0; i < count; i++ { + for i := range count { if firstParts[i] != secondParts[i] { break } diff --git a/lib/fs/walkfs.go b/lib/fs/walkfs.go index 36bce97a7..a6c8613bb 100644 --- a/lib/fs/walkfs.go +++ b/lib/fs/walkfs.go @@ -89,7 +89,7 @@ func (f *walkFilesystem) walk(path string, info FileInfo, walkFn WalkFunc, ances err = walkFn(path, info, nil) if err != nil { - if info.IsDir() && err == SkipDir { + if info.IsDir() && errors.Is(err, SkipDir) { return nil } return err @@ -117,13 +117,13 @@ func (f *walkFilesystem) walk(path string, info FileInfo, walkFn WalkFunc, ances filename := filepath.Join(path, name) fileInfo, err := f.Lstat(filename) if err != nil { - if err := walkFn(filename, fileInfo, err); err != nil && err != SkipDir { + if err := walkFn(filename, fileInfo, err); err != nil && !errors.Is(err, SkipDir) { return err } } else { err = f.walk(filename, fileInfo, walkFn, ancestors) if err != nil { - if !fileInfo.IsDir() || err != SkipDir { + if !fileInfo.IsDir() || !errors.Is(err, SkipDir) { return err } } diff --git a/lib/locations/locations.go b/lib/locations/locations.go index bbf11d919..531be714e 100644 --- a/lib/locations/locations.go +++ b/lib/locations/locations.go @@ -22,18 +22,19 @@ type LocationEnum string // Use strings as keys to make printout and serialization of the locations map // more meaningful. const ( - ConfigFile LocationEnum = "config" - CertFile LocationEnum = "certFile" - KeyFile LocationEnum = "keyFile" - HTTPSCertFile LocationEnum = "httpsCertFile" - HTTPSKeyFile LocationEnum = "httpsKeyFile" - Database LocationEnum = "database" - LogFile LocationEnum = "logFile" - PanicLog LocationEnum = "panicLog" - AuditLog LocationEnum = "auditLog" - GUIAssets LocationEnum = "guiAssets" - DefFolder LocationEnum = "defFolder" - LockFile LocationEnum = "lockFile" + ConfigFile LocationEnum = "config" + CertFile LocationEnum = "certFile" + KeyFile LocationEnum = "keyFile" + HTTPSCertFile LocationEnum = "httpsCertFile" + HTTPSKeyFile LocationEnum = "httpsKeyFile" + LegacyDatabase LocationEnum = "legacyDatabase" + Database LocationEnum = "database" + LogFile LocationEnum = "logFile" + PanicLog LocationEnum = "panicLog" + AuditLog LocationEnum = "auditLog" + GUIAssets LocationEnum = "guiAssets" + DefFolder LocationEnum = "defFolder" + LockFile LocationEnum = "lockFile" ) type BaseDirEnum string @@ -47,14 +48,15 @@ const ( // User's home directory, *not* --home flag UserHomeBaseDir BaseDirEnum = "userHome" - LevelDBDir = "index-v0.14.0.db" + levelDBDir = "index-v0.14.0.db" + databaseName = "index-v2" configFileName = "config.xml" defaultStateDir = ".local/state/syncthing" oldDefaultConfigDir = ".config/syncthing" ) // Platform dependent directories -var baseDirs = make(map[BaseDirEnum]string, 3) +var baseDirs = make(map[BaseDirEnum]string) func init() { userHome := userHomeDir() @@ -114,18 +116,19 @@ func GetBaseDir(baseDir BaseDirEnum) string { // Use the variables from baseDirs here var locationTemplates = map[LocationEnum]string{ - ConfigFile: "${config}/config.xml", - CertFile: "${config}/cert.pem", - KeyFile: "${config}/key.pem", - HTTPSCertFile: "${config}/https-cert.pem", - HTTPSKeyFile: "${config}/https-key.pem", - Database: "${data}/" + LevelDBDir, - LogFile: "${data}/syncthing.log", // --logfile on Windows - PanicLog: "${data}/panic-%{timestamp}.log", - AuditLog: "${data}/audit-%{timestamp}.log", - GUIAssets: "${config}/gui", - DefFolder: "${userHome}/Sync", - LockFile: "${data}/syncthing.lock", + ConfigFile: "${config}/config.xml", + CertFile: "${config}/cert.pem", + KeyFile: "${config}/key.pem", + HTTPSCertFile: "${config}/https-cert.pem", + HTTPSKeyFile: "${config}/https-key.pem", + LegacyDatabase: "${data}/" + levelDBDir, + Database: "${data}/" + databaseName, + LogFile: "${data}/syncthing.log", // --logfile on Windows + PanicLog: "${data}/panic-%{timestamp}.log", + AuditLog: "${data}/audit-%{timestamp}.log", + GUIAssets: "${config}/gui", + DefFolder: "${userHome}/Sync", + LockFile: "${data}/syncthing.lock", } var locations = make(map[LocationEnum]string) @@ -244,7 +247,8 @@ func unixDataDir(userHome, configDir, xdgDataHome, xdgStateHome string, fileExis // If a database exists at the config location, use that. This is the // most common case for both legacy (~/.config/syncthing) and current // (~/.local/state/syncthing) setups. - if fileExists(filepath.Join(configDir, LevelDBDir)) { + if fileExists(filepath.Join(configDir, databaseName)) || + fileExists(filepath.Join(configDir, levelDBDir)) { return configDir } @@ -253,14 +257,16 @@ func unixDataDir(userHome, configDir, xdgDataHome, xdgStateHome string, fileExis // but that's not what we did previously, so we retain the old behavior. if xdgDataHome != "" { candidate := filepath.Join(xdgDataHome, "syncthing") - if fileExists(filepath.Join(candidate, LevelDBDir)) { + if fileExists(filepath.Join(candidate, databaseName)) || + fileExists(filepath.Join(candidate, levelDBDir)) { return candidate } } // Legacy: if a database exists under ~/.config/syncthing, use that candidate := filepath.Join(userHome, oldDefaultConfigDir) - if fileExists(filepath.Join(candidate, LevelDBDir)) { + if fileExists(filepath.Join(candidate, databaseName)) || + fileExists(filepath.Join(candidate, levelDBDir)) { return candidate } diff --git a/lib/model/blockpullreorderer.go b/lib/model/blockpullreorderer.go index 838fa869d..d566972e2 100644 --- a/lib/model/blockpullreorderer.go +++ b/lib/model/blockpullreorderer.go @@ -51,7 +51,7 @@ type standardBlockPullReorderer struct { } func newStandardBlockPullReorderer(id protocol.DeviceID, otherDevices []protocol.DeviceID) *standardBlockPullReorderer { - allDevices := append(otherDevices, id) + allDevices := append(otherDevices, id) //nolint:gocritic slices.SortFunc(allDevices, func(a, b protocol.DeviceID) int { return a.Compare(b) }) @@ -92,7 +92,7 @@ func (p *standardBlockPullReorderer) Reorder(blocks []protocol.BlockInfo) []prot // The rest of the chunks we fetch in a random order in whole chunks. // Generate chunk index slice and shuffle it indexes := make([]int, 0, len(chunks)-1) - for i := 0; i < len(chunks); i++ { + for i := range chunks { if i != p.myIndex { indexes = append(indexes, i) } diff --git a/lib/model/devicedownloadstate.go b/lib/model/devicedownloadstate.go index 13059499a..a76fd0c94 100644 --- a/lib/model/devicedownloadstate.go +++ b/lib/model/devicedownloadstate.go @@ -56,17 +56,18 @@ func (p *deviceFolderDownloadState) Update(updates []protocol.FileDownloadProgre if update.UpdateType == protocol.FileDownloadProgressUpdateTypeForget && ok && local.version.Equal(update.Version) { delete(p.files, update.Name) } else if update.UpdateType == protocol.FileDownloadProgressUpdateTypeAppend { - if !ok { + switch { + case !ok: local = deviceFolderFileDownloadState{ blockIndexes: update.BlockIndexes, version: update.Version, - blockSize: int(update.BlockSize), + blockSize: update.BlockSize, } - } else if !local.version.Equal(update.Version) { + case !local.version.Equal(update.Version): local.blockIndexes = append(local.blockIndexes[:0], update.BlockIndexes...) local.version = update.Version - local.blockSize = int(update.BlockSize) - } else { + local.blockSize = update.BlockSize + default: local.blockIndexes = append(local.blockIndexes, update.BlockIndexes...) } p.files[update.Name] = local diff --git a/lib/model/fakeconns_test.go b/lib/model/fakeconns_test.go index 0d929bb82..524aa62d5 100644 --- a/lib/model/fakeconns_test.go +++ b/lib/model/fakeconns_test.go @@ -12,6 +12,7 @@ import ( "sync" "time" + "github.com/syncthing/syncthing/internal/timeutil" "github.com/syncthing/syncthing/lib/protocol" protocolmocks "github.com/syncthing/syncthing/lib/protocol/mocks" "github.com/syncthing/syncthing/lib/rand" @@ -75,13 +76,13 @@ func (f *fakeConnection) DownloadProgress(_ context.Context, dp *protocol.Downlo func (f *fakeConnection) addFileLocked(name string, flags uint32, ftype protocol.FileInfoType, data []byte, version protocol.Vector, localFlags uint32) { blockSize := protocol.BlockSize(int64(len(data))) - blocks, _ := scanner.Blocks(context.TODO(), bytes.NewReader(data), blockSize, int64(len(data)), nil, true) + blocks, _ := scanner.Blocks(context.TODO(), bytes.NewReader(data), blockSize, int64(len(data)), nil) file := protocol.FileInfo{ Name: name, Type: ftype, Version: version, - Sequence: time.Now().UnixNano(), + Sequence: timeutil.StrictlyMonotonicNanos(), LocalFlags: localFlags, } switch ftype { @@ -108,15 +109,6 @@ func (f *fakeConnection) addFileLocked(name string, flags uint32, ftype protocol f.fileData[name] = data } -func (f *fakeConnection) addFileWithLocalFlags(name string, ftype protocol.FileInfoType, localFlags uint32) { - f.mut.Lock() - defer f.mut.Unlock() - - var version protocol.Vector - version = version.Update(f.id.Short()) - f.addFileLocked(name, 0, ftype, nil, version, localFlags) -} - func (f *fakeConnection) addFile(name string, flags uint32, ftype protocol.FileInfoType, data []byte) { f.mut.Lock() defer f.mut.Unlock() @@ -148,7 +140,7 @@ func (f *fakeConnection) deleteFile(name string) { fi.Deleted = true fi.ModifiedS = time.Now().Unix() fi.Version = fi.Version.Update(f.id.Short()) - fi.Sequence = time.Now().UnixNano() + fi.Sequence = timeutil.StrictlyMonotonicNanos() fi.Blocks = nil f.files = append(append(f.files[:i], f.files[i+1:]...), fi) diff --git a/lib/db/util.go b/lib/model/fileinfobatch.go similarity index 99% rename from lib/db/util.go rename to lib/model/fileinfobatch.go index 9251f3fdc..923988489 100644 --- a/lib/db/util.go +++ b/lib/model/fileinfobatch.go @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at https://mozilla.org/MPL/2.0/. -package db +package model import ( "github.com/syncthing/syncthing/lib/protocol" diff --git a/lib/model/fileinfobatch_test.go b/lib/model/fileinfobatch_test.go new file mode 100644 index 000000000..2ed7d9752 --- /dev/null +++ b/lib/model/fileinfobatch_test.go @@ -0,0 +1,64 @@ +// Copyright (C) 2018 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +package model + +import ( + "errors" + "testing" + + "github.com/syncthing/syncthing/lib/protocol" +) + +func TestFileInfoBatchError(t *testing.T) { + // Verify behaviour of the flush function returning an error. + + var errReturn error + var called int + b := NewFileInfoBatch(func([]protocol.FileInfo) error { + called += 1 + return errReturn + }) + + // Flush should work when the flush function error is nil + b.Append(protocol.FileInfo{Name: "test"}) + if err := b.Flush(); err != nil { + t.Fatalf("expected nil, got %v", err) + } + if called != 1 { + t.Fatalf("expected 1, got %d", called) + } + + // Flush should fail with an error retur + errReturn = errors.New("problem") + b.Append(protocol.FileInfo{Name: "test"}) + if err := b.Flush(); err != errReturn { + t.Fatalf("expected %v, got %v", errReturn, err) + } + if called != 2 { + t.Fatalf("expected 2, got %d", called) + } + + // Flush function should not be called again when it's already errored, + // same error should be returned by Flush() + if err := b.Flush(); err != errReturn { + t.Fatalf("expected %v, got %v", errReturn, err) + } + if called != 2 { + t.Fatalf("expected 2, got %d", called) + } + + // Reset should clear the error (and the file list) + errReturn = nil + b.Reset() + b.Append(protocol.FileInfo{Name: "test"}) + if err := b.Flush(); err != nil { + t.Fatalf("expected nil, got %v", err) + } + if called != 3 { + t.Fatalf("expected 3, got %d", called) + } +} diff --git a/lib/model/folder.go b/lib/model/folder.go index 863721c2c..8d98e8495 100644 --- a/lib/model/folder.go +++ b/lib/model/folder.go @@ -16,8 +16,9 @@ import ( "strings" "time" + "github.com/syncthing/syncthing/internal/db" + "github.com/syncthing/syncthing/internal/itererr" "github.com/syncthing/syncthing/lib/config" - "github.com/syncthing/syncthing/lib/db" "github.com/syncthing/syncthing/lib/events" "github.com/syncthing/syncthing/lib/fs" "github.com/syncthing/syncthing/lib/ignore" @@ -47,11 +48,11 @@ type folder struct { model *model shortID protocol.ShortID - fset *db.FileSet + db db.DB ignores *ignore.Matcher mtimefs fs.Filesystem modTimeWindow time.Duration - ctx context.Context // used internally, only accessible on serve lifetime + ctx context.Context //nolint:containedctx // used internally, only accessible on serve lifetime done chan struct{} // used externally, accessible regardless of serve scanInterval time.Duration @@ -97,18 +98,18 @@ type puller interface { pull() (bool, error) // true when successful and should not be retried } -func newFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, evLogger events.Logger, ioLimiter *semaphore.Semaphore, ver versioner.Versioner) folder { +func newFolder(model *model, ignores *ignore.Matcher, cfg config.FolderConfiguration, evLogger events.Logger, ioLimiter *semaphore.Semaphore, ver versioner.Versioner) folder { f := folder{ stateTracker: newStateTracker(cfg.ID, evLogger), FolderConfiguration: cfg, - FolderStatisticsReference: stats.NewFolderStatisticsReference(model.db, cfg.ID), + FolderStatisticsReference: stats.NewFolderStatisticsReference(db.NewTyped(model.sdb, "folderstats/"+cfg.ID)), ioLimiter: ioLimiter, model: model, shortID: model.shortID, - fset: fset, + db: model.sdb, ignores: ignores, - mtimefs: cfg.Filesystem(fset), + mtimefs: cfg.Filesystem(fs.NewMtimeOption(model.sdb, cfg.ID)), modTimeWindow: cfg.ModTimeWindow(), done: make(chan struct{}), @@ -173,6 +174,8 @@ func (f *folder) Serve(ctx context.Context) error { } initialCompleted := f.initialScanFinished + pullTimer := time.NewTimer(0) + pullTimer.Stop() for { var err error @@ -183,6 +186,17 @@ func (f *folder) Serve(ctx context.Context) error { return nil case <-f.pullScheduled: + if f.PullerDelayS > 0 { + // Wait for incoming updates to settle before doing the + // actual pull + f.setState(FolderSyncWaiting) + pullTimer.Reset(time.Duration(float64(time.Second) * f.PullerDelayS)) + } else { + _, err = f.pull() + } + + case <-pullTimer.C: + f.setState(FolderIdle) _, err = f.pull() case <-f.pullFailTimer.C: @@ -308,7 +322,7 @@ func (f *folder) Reschedule() { return } // Sleep a random time between 3/4 and 5/4 of the configured interval. - sleepNanos := (f.scanInterval.Nanoseconds()*3 + rand.Int63n(2*f.scanInterval.Nanoseconds())) / 4 + sleepNanos := (f.scanInterval.Nanoseconds()*3 + rand.Int63n(2*f.scanInterval.Nanoseconds())) / 4 //nolint:gosec interval := time.Duration(sleepNanos) * time.Nanosecond l.Debugln(f, "next rescan in", interval) f.scanTimer.Reset(interval) @@ -368,17 +382,11 @@ func (f *folder) pull() (success bool, err error) { }() // If there is nothing to do, don't even enter sync-waiting state. - abort := true - snap, err := f.dbSnapshot() + needCount, err := f.db.CountNeed(f.folderID, protocol.LocalDeviceID) if err != nil { return false, err } - snap.WithNeed(protocol.LocalDeviceID, func(intf protocol.FileInfo) bool { - abort = false - return false - }) - snap.Release() - if abort { + if needCount.TotalItems() == 0 { // Clears pull failures on items that were needed before, but aren't anymore. f.errorsMut.Lock() f.pullErrors = nil @@ -485,15 +493,10 @@ func (f *folder) scanSubdirs(subDirs []string) error { // Clean the list of subitems to ensure that we start at a known // directory, and don't scan subdirectories of things we've already // scanned. - snap, err := f.dbSnapshot() - if err != nil { - return err - } subDirs = unifySubs(subDirs, func(file string) bool { - _, ok := snap.Get(protocol.LocalDeviceID, file) - return ok + _, ok, err := f.db.GetDeviceFile(f.folderID, protocol.LocalDeviceID, file) + return err == nil && ok }) - snap.Release() f.setState(FolderScanning) f.clearScanErrors(subDirs) @@ -547,7 +550,7 @@ const maxToRemove = 1000 type scanBatch struct { f *folder - updateBatch *db.FileInfoBatch + updateBatch *FileInfoBatch toRemove []string } @@ -556,7 +559,7 @@ func (f *folder) newScanBatch() *scanBatch { f: f, toRemove: make([]string, 0, maxToRemove), } - b.updateBatch = db.NewFileInfoBatch(func(fs []protocol.FileInfo) error { + b.updateBatch = NewFileInfoBatch(func(fs []protocol.FileInfo) error { if err := b.f.getHealthErrorWithoutIgnores(); err != nil { l.Debugf("Stopping scan of folder %s due to: %s", b.f.Description(), err) return err @@ -571,46 +574,56 @@ func (b *scanBatch) Remove(item string) { b.toRemove = append(b.toRemove, item) } -func (b *scanBatch) flushToRemove() { +func (b *scanBatch) flushToRemove() error { if len(b.toRemove) > 0 { - b.f.fset.RemoveLocalItems(b.toRemove) + if err := b.f.db.DropFilesNamed(b.f.folderID, protocol.LocalDeviceID, b.toRemove); err != nil { + return err + } b.toRemove = b.toRemove[:0] } + return nil } func (b *scanBatch) Flush() error { - b.flushToRemove() + if err := b.flushToRemove(); err != nil { + return err + } return b.updateBatch.Flush() } func (b *scanBatch) FlushIfFull() error { if len(b.toRemove) >= maxToRemove { - b.flushToRemove() + if err := b.flushToRemove(); err != nil { + return err + } } return b.updateBatch.FlushIfFull() } // Update adds the fileinfo to the batch for updating, and does a few checks. // It returns false if the checks result in the file not going to be updated or removed. -func (b *scanBatch) Update(fi protocol.FileInfo, snap *db.Snapshot) bool { +func (b *scanBatch) Update(fi protocol.FileInfo) (bool, error) { // Check for a "virtual" parent directory of encrypted files. We don't track // it, but check if anything still exists within and delete it otherwise. if b.f.Type == config.FolderTypeReceiveEncrypted && fi.IsDirectory() && protocol.IsEncryptedParent(fs.PathComponents(fi.Name)) { if names, err := b.f.mtimefs.DirNames(fi.Name); err == nil && len(names) == 0 { b.f.mtimefs.Remove(fi.Name) } - return false + return false, nil } // Resolve receive-only items which are identical with the global state or // the global item is our own receive-only item. - switch gf, ok := snap.GetGlobal(fi.Name); { + switch gf, ok, err := b.f.db.GetGlobalFile(b.f.folderID, fi.Name); { + case err != nil: + return false, err case !ok: case gf.IsReceiveOnlyChanged(): if fi.IsDeleted() { // Our item is deleted and the global item is our own receive only // file. No point in keeping track of that. b.Remove(fi.Name) - return true + l.Debugf("%v scanning: deleting deleted receive-only local-changed file: %v", b.f, fi) + return true, nil } case (b.f.Type == config.FolderTypeReceiveOnly || b.f.Type == config.FolderTypeReceiveEncrypted) && gf.IsEquivalentOptional(fi, protocol.FileInfoComparison{ @@ -622,20 +635,15 @@ func (b *scanBatch) Update(fi protocol.FileInfo, snap *db.Snapshot) bool { IgnoreXattrs: !b.f.SyncXattrs && !b.f.SendXattrs, }): // What we have locally is equivalent to the global file. - l.Debugf("%v scanning: Merging identical locally changed item with global", b.f, fi) + l.Debugf("%v scanning: Merging identical locally changed item with global: %v", b.f, fi) fi = gf } b.updateBatch.Append(fi) - return true + return true, nil } func (f *folder) scanSubdirsChangedAndNew(subDirs []string, batch *scanBatch) (int, error) { changes := 0 - snap, err := f.dbSnapshot() - if err != nil { - return changes, err - } - defer snap.Release() // If we return early e.g. due to a folder health error, the scan needs // to be cancelled. @@ -647,7 +655,7 @@ func (f *folder) scanSubdirsChangedAndNew(subDirs []string, batch *scanBatch) (i Subs: subDirs, Matcher: f.ignores, TempLifetime: time.Duration(f.model.cfg.Options().KeepTemporariesH) * time.Hour, - CurrentFiler: cFiler{snap}, + CurrentFiler: cFiler{db: f.db, folder: f.folderID}, Filesystem: f.mtimefs, IgnorePerms: f.IgnorePerms, AutoNormalize: f.AutoNormalize, @@ -684,15 +692,19 @@ func (f *folder) scanSubdirsChangedAndNew(subDirs []string, batch *scanBatch) (i return changes, err } - if batch.Update(res.File, snap) { + if ok, err := batch.Update(res.File); err != nil { + return 0, err + } else if ok { changes++ } switch f.Type { case config.FolderTypeReceiveOnly, config.FolderTypeReceiveEncrypted: default: - if nf, ok := f.findRename(snap, res.File, alreadyUsedOrExisting); ok { - if batch.Update(nf, snap) { + if nf, ok := f.findRename(res.File, alreadyUsedOrExisting); ok { + if ok, err := batch.Update(nf); err != nil { + return 0, err + } else if ok { changes++ } } @@ -706,25 +718,22 @@ func (f *folder) scanSubdirsDeletedAndIgnored(subDirs []string, batch *scanBatch var toIgnore []protocol.FileInfo ignoredParent := "" changes := 0 - snap, err := f.dbSnapshot() - if err != nil { - return 0, err - } - defer snap.Release() +outer: for _, sub := range subDirs { - var iterError error + for fi, err := range itererr.Zip(f.db.AllLocalFilesWithPrefix(f.folderID, protocol.LocalDeviceID, sub)) { + if err != nil { + return changes, err + } - snap.WithPrefixedHaveTruncated(protocol.LocalDeviceID, sub, func(fi protocol.FileInfo) bool { select { case <-f.ctx.Done(): - return false + break outer default: } if err := batch.FlushIfFull(); err != nil { - iterError = err - return false + return 0, err } if ignoredParent != "" && !fs.IsParent(fi.Name, ignoredParent) { @@ -732,12 +741,13 @@ func (f *folder) scanSubdirsDeletedAndIgnored(subDirs []string, batch *scanBatch l.Debugln("marking file as ignored", file) nf := file nf.SetIgnored() - if batch.Update(nf, snap) { + if ok, err := batch.Update(nf); err != nil { + return 0, err + } else if ok { changes++ } if err := batch.FlushIfFull(); err != nil { - iterError = err - return false + return 0, err } } toIgnore = toIgnore[:0] @@ -746,7 +756,7 @@ func (f *folder) scanSubdirsDeletedAndIgnored(subDirs []string, batch *scanBatch switch ignored := f.ignores.Match(fi.Name).IsIgnored(); { case fi.IsIgnored() && ignored: - return true + continue case !fi.IsIgnored() && ignored: // File was not ignored at last pass but has been ignored. if fi.IsDirectory() { @@ -757,13 +767,15 @@ func (f *folder) scanSubdirsDeletedAndIgnored(subDirs []string, batch *scanBatch // this path as the "highest" ignored parent ignoredParent = fi.Name } - return true + continue } l.Debugln("marking file as ignored", fi) nf := fi nf.SetIgnored() - if batch.Update(nf, snap) { + if ok, err := batch.Update(nf); err != nil { + return 0, err + } else if ok { changes++ } @@ -782,7 +794,7 @@ func (f *folder) scanSubdirsDeletedAndIgnored(subDirs []string, batch *scanBatch toIgnore = toIgnore[:0] ignoredParent = "" } - return true + continue } nf := fi nf.SetDeleted(f.shortID) @@ -794,13 +806,17 @@ func (f *folder) scanSubdirsDeletedAndIgnored(subDirs []string, batch *scanBatch nf.Version = protocol.Vector{} } l.Debugln("marking file as deleted", nf) - if batch.Update(nf, snap) { + if ok, err := batch.Update(nf); err != nil { + return 0, err + } else if ok { changes++ } case fi.IsDeleted() && fi.IsReceiveOnlyChanged(): switch f.Type { case config.FolderTypeReceiveOnly, config.FolderTypeReceiveEncrypted: - switch gf, ok := snap.GetGlobal(fi.Name); { + switch gf, ok, err := f.db.GetGlobalFile(f.folderID, fi.Name); { + case err != nil: + return 0, err case !ok: case gf.IsReceiveOnlyChanged(): l.Debugln("removing deleted, receive-only item that is globally receive-only from db", fi) @@ -811,7 +827,9 @@ func (f *folder) scanSubdirsDeletedAndIgnored(subDirs []string, batch *scanBatch // pretend it is a normal deleted file (nobody cares about that). l.Debugf("%v scanning: Marking globally deleted item as not locally changed: %v", f, fi.Name) fi.LocalFlags &^= protocol.FlagLocalReceiveOnly - if batch.Update(fi, snap) { + if ok, err := batch.Update(fi); err != nil { + return 0, err + } else if ok { changes++ } } @@ -820,14 +838,14 @@ func (f *folder) scanSubdirsDeletedAndIgnored(subDirs []string, batch *scanBatch // deleted and just the folder type/local flags changed. fi.LocalFlags &^= protocol.FlagLocalReceiveOnly l.Debugln("removing receive-only flag on deleted item", fi) - if batch.Update(fi, snap) { + if ok, err := batch.Update(fi); err != nil { + return 0, err + } else if ok { changes++ } } } - - return true - }) + } select { case <-f.ctx.Done(): @@ -835,30 +853,28 @@ func (f *folder) scanSubdirsDeletedAndIgnored(subDirs []string, batch *scanBatch default: } - if iterError == nil && len(toIgnore) > 0 { + if len(toIgnore) > 0 { for _, file := range toIgnore { l.Debugln("marking file as ignored", file) nf := file nf.SetIgnored() - if batch.Update(nf, snap) { + if ok, err := batch.Update(nf); err != nil { + return 0, err + } else if ok { changes++ } - if iterError = batch.FlushIfFull(); iterError != nil { - break + if err := batch.FlushIfFull(); err != nil { + return 0, err } } toIgnore = toIgnore[:0] } - - if iterError != nil { - return changes, iterError - } } return changes, nil } -func (f *folder) findRename(snap *db.Snapshot, file protocol.FileInfo, alreadyUsedOrExisting map[string]struct{}) (protocol.FileInfo, bool) { +func (f *folder) findRename(file protocol.FileInfo, alreadyUsedOrExisting map[string]struct{}) (protocol.FileInfo, bool) { if len(file.Blocks) == 0 || file.Size == 0 { return protocol.FileInfo{}, false } @@ -866,49 +882,58 @@ func (f *folder) findRename(snap *db.Snapshot, file protocol.FileInfo, alreadyUs found := false nf := protocol.FileInfo{} - snap.WithBlocksHash(file.BlocksHash, func(fi protocol.FileInfo) bool { +loop: + for fi, err := range itererr.Zip(f.db.AllLocalFilesWithBlocksHash(f.folderID, file.BlocksHash)) { + if err != nil { + return protocol.FileInfo{}, false + } + select { case <-f.ctx.Done(): - return false + break loop default: } if fi.Name == file.Name { alreadyUsedOrExisting[fi.Name] = struct{}{} - return true + continue } if _, ok := alreadyUsedOrExisting[fi.Name]; ok { - return true + continue } if fi.ShouldConflict() { - return true + continue } if f.ignores.Match(fi.Name).IsIgnored() { - return true + continue } // Only check the size. // No point checking block equality, as that uses BlocksHash comparison if that is set (which it will be). // No point checking BlocksHash comparison as WithBlocksHash already does that. if file.Size != fi.Size { - return true + continue } alreadyUsedOrExisting[fi.Name] = struct{}{} if !osutil.IsDeleted(f.mtimefs, fi.Name) { - return true + continue } - nf = fi + var ok bool + nf, ok, err = f.db.GetDeviceFile(f.folderID, protocol.LocalDeviceID, fi.Name) + if err != nil || !ok || nf.Sequence != fi.Sequence { + continue + } nf.SetDeleted(f.shortID) nf.LocalFlags = f.localFlags found = true - return false - }) + break + } return nf, found } @@ -1052,7 +1077,7 @@ func (f *folder) monitorWatch(ctx context.Context) { f.setWatchError(err, next) // This error was previously a panic and should never occur, so generate // a warning, but don't do it repetitively. - var errOutside *fs.ErrWatchEventOutsideRoot + var errOutside *fs.WatchEventOutsideRootError if errors.As(err, &errOutside) { if !warnedOutside { l.Warnln(err) @@ -1086,7 +1111,7 @@ func (f *folder) setWatchError(err error, nextTryIn time.Duration) { prevErr := f.watchErr f.watchErr = err f.watchMut.Unlock() - if err != prevErr { + if err != prevErr { //nolint:errorlint data := map[string]interface{}{ "folder": f.ID, } @@ -1102,7 +1127,7 @@ func (f *folder) setWatchError(err error, nextTryIn time.Duration) { return } msg := fmt.Sprintf("Error while trying to start filesystem watcher for folder %s, trying again in %v: %v", f.Description(), nextTryIn, err) - if prevErr != err { + if prevErr != err { //nolint:errorlint l.Infof(msg) return } @@ -1219,20 +1244,26 @@ func (f *folder) ScheduleForceRescan(path string) { } } -func (f *folder) updateLocalsFromScanning(fs []protocol.FileInfo) { - f.updateLocals(fs) - +func (f *folder) updateLocalsFromScanning(fs []protocol.FileInfo) error { + if err := f.updateLocals(fs); err != nil { + return err + } f.emitDiskChangeEvents(fs, events.LocalChangeDetected) + return nil } -func (f *folder) updateLocalsFromPulling(fs []protocol.FileInfo) { - f.updateLocals(fs) - +func (f *folder) updateLocalsFromPulling(fs []protocol.FileInfo) error { + if err := f.updateLocals(fs); err != nil { + return err + } f.emitDiskChangeEvents(fs, events.RemoteChangeDetected) + return nil } -func (f *folder) updateLocals(fs []protocol.FileInfo) { - f.fset.Update(protocol.LocalDeviceID, fs) +func (f *folder) updateLocals(fs []protocol.FileInfo) error { + if err := f.db.Update(f.folderID, protocol.LocalDeviceID, fs); err != nil { + return err + } filenames := make([]string, len(fs)) f.forcedRescanPathsMut.Lock() @@ -1243,7 +1274,10 @@ func (f *folder) updateLocals(fs []protocol.FileInfo) { } f.forcedRescanPathsMut.Unlock() - seq := f.fset.Sequence(protocol.LocalDeviceID) + seq, err := f.db.GetDeviceSequence(f.folderID, protocol.LocalDeviceID) + if err != nil { + return err + } f.evLogger.Log(events.LocalIndexUpdated, map[string]interface{}{ "folder": f.ID, "items": len(fs), @@ -1251,6 +1285,7 @@ func (f *folder) updateLocals(fs []protocol.FileInfo) { "sequence": seq, "version": seq, // legacy for sequence }) + return nil } func (f *folder) emitDiskChangeEvents(fs []protocol.FileInfo, typeOfEvent events.EventType) { @@ -1297,23 +1332,19 @@ func (f *folder) handleForcedRescans() error { return nil } - batch := db.NewFileInfoBatch(func(fs []protocol.FileInfo) error { - f.fset.Update(protocol.LocalDeviceID, fs) - return nil + batch := NewFileInfoBatch(func(fs []protocol.FileInfo) error { + return f.db.Update(f.folderID, protocol.LocalDeviceID, fs) }) - snap, err := f.dbSnapshot() - if err != nil { - return err - } - defer snap.Release() - for _, path := range paths { if err := batch.FlushIfFull(); err != nil { return err } - fi, ok := snap.Get(protocol.LocalDeviceID, path) + fi, ok, err := f.db.GetDeviceFile(f.folderID, protocol.LocalDeviceID, path) + if err != nil { + return err + } if !ok { continue } @@ -1321,23 +1352,13 @@ func (f *folder) handleForcedRescans() error { batch.Append(fi) } - if err = batch.Flush(); err != nil { + if err := batch.Flush(); err != nil { return err } return f.scanSubdirs(paths) } -// dbSnapshots gets a snapshot from the fileset, and wraps any error -// in a svcutil.FatalErr. -func (f *folder) dbSnapshot() (*db.Snapshot, error) { - snap, err := f.fset.Snapshot() - if err != nil { - return nil, svcutil.AsFatalErr(err, svcutil.ExitError) - } - return snap, nil -} - // The exists function is expected to return true for all known paths // (excluding "" and ".") func unifySubs(dirs []string, exists func(dir string) bool) []string { @@ -1373,10 +1394,15 @@ func unifySubs(dirs []string, exists func(dir string) bool) []string { } type cFiler struct { - *db.Snapshot + db db.DB + folder string } // Implements scanner.CurrentFiler func (cf cFiler) CurrentFile(file string) (protocol.FileInfo, bool) { - return cf.Get(protocol.LocalDeviceID, file) + fi, ok, err := cf.db.GetDeviceFile(cf.folder, protocol.LocalDeviceID, file) + if err != nil || !ok { + return protocol.FileInfo{}, false + } + return fi, true } diff --git a/lib/model/folder_recvenc.go b/lib/model/folder_recvenc.go index 7e3cd09de..bc5e5b701 100644 --- a/lib/model/folder_recvenc.go +++ b/lib/model/folder_recvenc.go @@ -11,8 +11,8 @@ import ( "slices" "strings" + "github.com/syncthing/syncthing/internal/itererr" "github.com/syncthing/syncthing/lib/config" - "github.com/syncthing/syncthing/lib/db" "github.com/syncthing/syncthing/lib/events" "github.com/syncthing/syncthing/lib/fs" "github.com/syncthing/syncthing/lib/ignore" @@ -29,8 +29,8 @@ type receiveEncryptedFolder struct { *sendReceiveFolder } -func newReceiveEncryptedFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, evLogger events.Logger, ioLimiter *semaphore.Semaphore) service { - f := &receiveEncryptedFolder{newSendReceiveFolder(model, fset, ignores, cfg, ver, evLogger, ioLimiter).(*sendReceiveFolder)} +func newReceiveEncryptedFolder(model *model, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, evLogger events.Logger, ioLimiter *semaphore.Semaphore) service { + f := &receiveEncryptedFolder{newSendReceiveFolder(model, ignores, cfg, ver, evLogger, ioLimiter).(*sendReceiveFolder)} f.localFlags = protocol.FlagLocalReceiveOnly // gets propagated to the scanner, and set on locally changed files return f } @@ -45,30 +45,27 @@ func (f *receiveEncryptedFolder) revert() error { f.setState(FolderScanning) defer f.setState(FolderIdle) - batch := db.NewFileInfoBatch(func(fs []protocol.FileInfo) error { + batch := NewFileInfoBatch(func(fs []protocol.FileInfo) error { f.updateLocalsFromScanning(fs) return nil }) - snap, err := f.dbSnapshot() - if err != nil { - return err - } - defer snap.Release() - var iterErr error var dirs []string - snap.WithHaveTruncated(protocol.LocalDeviceID, func(fi protocol.FileInfo) bool { - if iterErr = batch.FlushIfFull(); iterErr != nil { - return false + for fi, err := range itererr.Zip(f.db.AllLocalFiles(f.folderID, protocol.LocalDeviceID)) { + if err != nil { + return err + } + if err := batch.FlushIfFull(); err != nil { + return err } if !fi.IsReceiveOnlyChanged() || fi.IsDeleted() { - return true + continue } if fi.IsDirectory() { dirs = append(dirs, fi.Name) - return true + continue } if err := f.inWritableDir(f.mtimefs.Remove, fi.Name); err != nil && !fs.IsNotExist(err) { @@ -85,15 +82,10 @@ func (f *receiveEncryptedFolder) revert() error { // deleted, it will not show up as an unexpected file in the UI // anymore. batch.Append(fi) - - return true - }) - - f.revertHandleDirs(dirs, snap) - - if iterErr != nil { - return iterErr } + + f.revertHandleDirs(dirs) + if err := batch.Flush(); err != nil { return err } @@ -104,7 +96,7 @@ func (f *receiveEncryptedFolder) revert() error { return nil } -func (f *receiveEncryptedFolder) revertHandleDirs(dirs []string, snap *db.Snapshot) { +func (f *receiveEncryptedFolder) revertHandleDirs(dirs []string) { if len(dirs) == 0 { return } @@ -117,7 +109,7 @@ func (f *receiveEncryptedFolder) revertHandleDirs(dirs []string, snap *db.Snapsh return strings.Compare(b, a) }) for _, dir := range dirs { - if err := f.deleteDirOnDisk(dir, snap, scanChan); err != nil { + if err := f.deleteDirOnDisk(dir, scanChan); err != nil { f.newScanError(dir, fmt.Errorf("deleting unexpected dir: %w", err)) } scanChan <- dir diff --git a/lib/model/folder_recvonly.go b/lib/model/folder_recvonly.go index eca6ebb44..1ad611b6d 100644 --- a/lib/model/folder_recvonly.go +++ b/lib/model/folder_recvonly.go @@ -11,8 +11,8 @@ import ( "strings" "time" + "github.com/syncthing/syncthing/internal/itererr" "github.com/syncthing/syncthing/lib/config" - "github.com/syncthing/syncthing/lib/db" "github.com/syncthing/syncthing/lib/events" "github.com/syncthing/syncthing/lib/ignore" "github.com/syncthing/syncthing/lib/protocol" @@ -58,8 +58,8 @@ type receiveOnlyFolder struct { *sendReceiveFolder } -func newReceiveOnlyFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, evLogger events.Logger, ioLimiter *semaphore.Semaphore) service { - sr := newSendReceiveFolder(model, fset, ignores, cfg, ver, evLogger, ioLimiter).(*sendReceiveFolder) +func newReceiveOnlyFolder(model *model, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, evLogger events.Logger, ioLimiter *semaphore.Semaphore) service { + sr := newSendReceiveFolder(model, ignores, cfg, ver, evLogger, ioLimiter).(*sendReceiveFolder) sr.localFlags = protocol.FlagLocalReceiveOnly // gets propagated to the scanner, and set on locally changed files return &receiveOnlyFolder{sr} } @@ -84,30 +84,31 @@ func (f *receiveOnlyFolder) revert() error { scanChan: scanChan, } - batch := db.NewFileInfoBatch(func(files []protocol.FileInfo) error { + batch := NewFileInfoBatch(func(files []protocol.FileInfo) error { f.updateLocalsFromScanning(files) return nil }) - snap, err := f.dbSnapshot() - if err != nil { - return err - } - defer snap.Release() - snap.WithHave(protocol.LocalDeviceID, func(fi protocol.FileInfo) bool { + + for fi, err := range itererr.Zip(f.db.AllLocalFiles(f.folderID, protocol.LocalDeviceID)) { + if err != nil { + return err + } if !fi.IsReceiveOnlyChanged() { // We're only interested in files that have changed locally in // receive only mode. - return true + continue } fi.LocalFlags &^= protocol.FlagLocalReceiveOnly - switch gf, ok := snap.GetGlobal(fi.Name); { + switch gf, ok, err := f.db.GetGlobalFile(f.folderID, fi.Name); { + case err != nil: + return err case !ok: - msg := "Unexpected global file that we have locally" + msg := "Unexpectedly missing global file that we have locally" l.Debugf("%v revert: %v: %v", f, msg, fi.Name) f.evLogger.Log(events.Failure, msg) - return true + continue case gf.IsReceiveOnlyChanged(): // The global file is our own. A revert then means to delete it. // We'll delete files directly, directories get queued and @@ -116,13 +117,13 @@ func (f *receiveOnlyFolder) revert() error { fi.Version = protocol.Vector{} // if this file ever resurfaces anywhere we want our delete to be strictly older break } - handled, err := delQueue.handle(fi, snap) + l.Debugf("Revert: deleting %s: %v\n", fi.Name, err) + handled, err := delQueue.handle(fi) if err != nil { - l.Infof("Revert: deleting %s: %v\n", fi.Name, err) - return true // continue + continue } if !handled { - return true // continue + continue } fi.SetDeleted(f.shortID) fi.Version = protocol.Vector{} // if this file ever resurfaces anywhere we want our delete to be strictly older @@ -145,13 +146,13 @@ func (f *receiveOnlyFolder) revert() error { batch.Append(fi) _ = batch.FlushIfFull() - - return true - }) - _ = batch.Flush() + } + if err := batch.Flush(); err != nil { + return err + } // Handle any queued directories - deleted, err := delQueue.flush(snap) + deleted, err := delQueue.flush() if err != nil { l.Infoln("Revert:", err) } @@ -180,15 +181,15 @@ func (f *receiveOnlyFolder) revert() error { // directories for last. type deleteQueue struct { handler interface { - deleteItemOnDisk(item protocol.FileInfo, snap *db.Snapshot, scanChan chan<- string) error - deleteDirOnDisk(dir string, snap *db.Snapshot, scanChan chan<- string) error + deleteItemOnDisk(item protocol.FileInfo, scanChan chan<- string) error + deleteDirOnDisk(dir string, scanChan chan<- string) error } ignores *ignore.Matcher dirs []string scanChan chan<- string } -func (q *deleteQueue) handle(fi protocol.FileInfo, snap *db.Snapshot) (bool, error) { +func (q *deleteQueue) handle(fi protocol.FileInfo) (bool, error) { // Things that are ignored but not marked deletable are not processed. ign := q.ignores.Match(fi.Name) if ign.IsIgnored() && !ign.IsDeletable() { @@ -202,11 +203,11 @@ func (q *deleteQueue) handle(fi protocol.FileInfo, snap *db.Snapshot) (bool, err } // Kill it. - err := q.handler.deleteItemOnDisk(fi, snap, q.scanChan) + err := q.handler.deleteItemOnDisk(fi, q.scanChan) return true, err } -func (q *deleteQueue) flush(snap *db.Snapshot) ([]string, error) { +func (q *deleteQueue) flush() ([]string, error) { // Process directories from the leaves inward. slices.SortFunc(q.dirs, func(a, b string) int { return strings.Compare(b, a) @@ -216,7 +217,7 @@ func (q *deleteQueue) flush(snap *db.Snapshot) ([]string, error) { var deleted []string for _, dir := range q.dirs { - if err := q.handler.deleteDirOnDisk(dir, snap, q.scanChan); err == nil { + if err := q.handler.deleteDirOnDisk(dir, q.scanChan); err == nil { deleted = append(deleted, dir) } else if firstError == nil { firstError = err diff --git a/lib/model/folder_recvonly_test.go b/lib/model/folder_recvonly_test.go index 534677ed2..3abf6bf08 100644 --- a/lib/model/folder_recvonly_test.go +++ b/lib/model/folder_recvonly_test.go @@ -13,6 +13,7 @@ import ( "testing" "time" + "github.com/syncthing/syncthing/internal/itererr" "github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/events" "github.com/syncthing/syncthing/lib/fs" @@ -28,7 +29,7 @@ func TestRecvOnlyRevertDeletes(t *testing.T) { m, f, wcfgCancel := setupROFolder(t) defer wcfgCancel() - ffs := f.Filesystem(nil) + ffs := f.Filesystem() defer cleanupModel(m) conn := addFakeConn(m, device1, f.ID) @@ -46,9 +47,11 @@ func TestRecvOnlyRevertDeletes(t *testing.T) { // Send and index update for the known stuff must(t, m.Index(conn, &protocol.Index{Folder: "ro", Files: knownFiles})) - f.updateLocalsFromScanning(knownFiles) + if err := f.updateLocalsFromScanning(knownFiles); err != nil { + t.Fatal(err) + } - size := globalSize(t, m, "ro") + size := mustV(m.GlobalSize("ro")) if size.Files != 1 || size.Directories != 1 { t.Fatalf("Global: expected 1 file and 1 directory: %+v", size) } @@ -59,15 +62,15 @@ func TestRecvOnlyRevertDeletes(t *testing.T) { // We should now have two files and two directories, with global state unchanged. - size = globalSize(t, m, "ro") + size = mustV(m.GlobalSize("ro")) if size.Files != 1 || size.Directories != 1 { - t.Fatalf("Global: expected 2 files and 2 directories: %+v", size) + t.Fatalf("Global: expected 1 file and 1 directory: %+v", size) } - size = localSize(t, m, "ro") + size = mustV(m.LocalSize("ro", protocol.LocalDeviceID)) if size.Files != 2 || size.Directories != 2 { t.Fatalf("Local: expected 2 files and 2 directories: %+v", size) } - size = receiveOnlyChangedSize(t, m, "ro") + size = mustV(m.ReceiveOnlySize("ro")) if size.Files+size.Directories == 0 { t.Fatalf("ROChanged: expected something: %+v", size) } @@ -92,11 +95,11 @@ func TestRecvOnlyRevertDeletes(t *testing.T) { // We should now have one file and directory again. - size = globalSize(t, m, "ro") + size = mustV(m.GlobalSize("ro")) if size.Files != 1 || size.Directories != 1 { t.Fatalf("Global: expected 1 files and 1 directories: %+v", size) } - size = localSize(t, m, "ro") + size = mustV(m.LocalSize("ro", protocol.LocalDeviceID)) if size.Files != 1 || size.Directories != 1 { t.Fatalf("Local: expected 1 files and 1 directories: %+v", size) } @@ -110,7 +113,7 @@ func TestRecvOnlyRevertNeeds(t *testing.T) { m, f, wcfgCancel := setupROFolder(t) defer wcfgCancel() - ffs := f.Filesystem(nil) + ffs := f.Filesystem() defer cleanupModel(m) conn := addFakeConn(m, device1, f.ID) @@ -131,19 +134,19 @@ func TestRecvOnlyRevertNeeds(t *testing.T) { // Everything should be in sync. - size := globalSize(t, m, "ro") + size := mustV(m.GlobalSize("ro")) if size.Files != 1 || size.Directories != 1 { t.Fatalf("Global: expected 1 file and 1 directory: %+v", size) } - size = localSize(t, m, "ro") + size = mustV(m.LocalSize("ro", protocol.LocalDeviceID)) if size.Files != 1 || size.Directories != 1 { t.Fatalf("Local: expected 1 file and 1 directory: %+v", size) } - size = needSizeLocal(t, m, "ro") + size = mustV(m.NeedSize("ro", protocol.LocalDeviceID)) if size.Files+size.Directories > 0 { t.Fatalf("Need: expected nothing: %+v", size) } - size = receiveOnlyChangedSize(t, m, "ro") + size = mustV(m.ReceiveOnlySize("ro")) if size.Files+size.Directories > 0 { t.Fatalf("ROChanged: expected nothing: %+v", size) } @@ -159,20 +162,20 @@ func TestRecvOnlyRevertNeeds(t *testing.T) { // We now have a newer file than the rest of the cluster. Global state should reflect this. - size = globalSize(t, m, "ro") + size = mustV(m.GlobalSize("ro")) const sizeOfDir = 128 if size.Files != 1 || size.Bytes != sizeOfDir+int64(len(oldData)) { t.Fatalf("Global: expected no change due to the new file: %+v", size) } - size = localSize(t, m, "ro") + size = mustV(m.LocalSize("ro", protocol.LocalDeviceID)) if size.Files != 1 || size.Bytes != sizeOfDir+int64(len(newData)) { t.Fatalf("Local: expected the new file to be reflected: %+v", size) } - size = needSizeLocal(t, m, "ro") + size = mustV(m.NeedSize("ro", protocol.LocalDeviceID)) if size.Files+size.Directories > 0 { t.Fatalf("Need: expected nothing: %+v", size) } - size = receiveOnlyChangedSize(t, m, "ro") + size = mustV(m.ReceiveOnlySize("ro")) if size.Files+size.Directories == 0 { t.Fatalf("ROChanged: expected something: %+v", size) } @@ -181,15 +184,15 @@ func TestRecvOnlyRevertNeeds(t *testing.T) { m.Revert("ro") - size = globalSize(t, m, "ro") + size = mustV(m.GlobalSize("ro")) if size.Files != 1 || size.Bytes != sizeOfDir+int64(len(oldData)) { t.Fatalf("Global: expected the global size to revert: %+v", size) } - size = localSize(t, m, "ro") + size = mustV(m.LocalSize("ro", protocol.LocalDeviceID)) if size.Files != 1 || size.Bytes != sizeOfDir+int64(len(newData)) { t.Fatalf("Local: expected the local size to remain: %+v", size) } - size = needSizeLocal(t, m, "ro") + size = mustV(m.NeedSize("ro", protocol.LocalDeviceID)) if size.Files != 1 || size.Bytes != int64(len(oldData)) { t.Fatalf("Local: expected to need the old file data: %+v", size) } @@ -200,7 +203,7 @@ func TestRecvOnlyUndoChanges(t *testing.T) { m, f, wcfgCancel := setupROFolder(t) defer wcfgCancel() - ffs := f.Filesystem(nil) + ffs := f.Filesystem() defer cleanupModel(m) conn := addFakeConn(m, device1, f.ID) @@ -221,19 +224,19 @@ func TestRecvOnlyUndoChanges(t *testing.T) { // Everything should be in sync. - size := globalSize(t, m, "ro") + size := mustV(m.GlobalSize("ro")) if size.Files != 1 || size.Directories != 1 { t.Fatalf("Global: expected 1 file and 1 directory: %+v", size) } - size = localSize(t, m, "ro") + size = mustV(m.LocalSize("ro", protocol.LocalDeviceID)) if size.Files != 1 || size.Directories != 1 { t.Fatalf("Local: expected 1 file and 1 directory: %+v", size) } - size = needSizeLocal(t, m, "ro") + size = mustV(m.NeedSize("ro", protocol.LocalDeviceID)) if size.Files+size.Directories > 0 { t.Fatalf("Need: expected nothing: %+v", size) } - size = receiveOnlyChangedSize(t, m, "ro") + size = mustV(m.ReceiveOnlySize("ro")) if size.Files+size.Directories > 0 { t.Fatalf("ROChanged: expected nothing: %+v", size) } @@ -246,7 +249,7 @@ func TestRecvOnlyUndoChanges(t *testing.T) { must(t, m.ScanFolder("ro")) - size = receiveOnlyChangedSize(t, m, "ro") + size = mustV(m.ReceiveOnlySize("ro")) if size.Files != 2 { t.Fatalf("Receive only: expected 2 files: %+v", size) } @@ -259,7 +262,7 @@ func TestRecvOnlyUndoChanges(t *testing.T) { must(t, m.ScanFolder("ro")) - size = receiveOnlyChangedSize(t, m, "ro") + size = mustV(m.ReceiveOnlySize("ro")) if size.Files+size.Directories+size.Deleted != 0 { t.Fatalf("Receive only: expected all zero: %+v", size) } @@ -270,7 +273,7 @@ func TestRecvOnlyDeletedRemoteDrop(t *testing.T) { m, f, wcfgCancel := setupROFolder(t) defer wcfgCancel() - ffs := f.Filesystem(nil) + ffs := f.Filesystem() defer cleanupModel(m) conn := addFakeConn(m, device1, f.ID) @@ -291,19 +294,19 @@ func TestRecvOnlyDeletedRemoteDrop(t *testing.T) { // Everything should be in sync. - size := globalSize(t, m, "ro") + size := mustV(m.GlobalSize("ro")) if size.Files != 1 || size.Directories != 1 { t.Fatalf("Global: expected 1 file and 1 directory: %+v", size) } - size = localSize(t, m, "ro") + size = mustV(m.LocalSize("ro", protocol.LocalDeviceID)) if size.Files != 1 || size.Directories != 1 { t.Fatalf("Local: expected 1 file and 1 directory: %+v", size) } - size = needSizeLocal(t, m, "ro") + size = mustV(m.NeedSize("ro", protocol.LocalDeviceID)) if size.Files+size.Directories > 0 { t.Fatalf("Need: expected nothing: %+v", size) } - size = receiveOnlyChangedSize(t, m, "ro") + size = mustV(m.ReceiveOnlySize("ro")) if size.Files+size.Directories > 0 { t.Fatalf("ROChanged: expected nothing: %+v", size) } @@ -314,17 +317,17 @@ func TestRecvOnlyDeletedRemoteDrop(t *testing.T) { must(t, m.ScanFolder("ro")) - size = receiveOnlyChangedSize(t, m, "ro") + size = mustV(m.ReceiveOnlySize("ro")) if size.Deleted != 1 { t.Fatalf("Receive only: expected 1 deleted: %+v", size) } // Drop the remote - f.fset.Drop(device1) + f.db.DropAllFiles("ro", device1) must(t, m.ScanFolder("ro")) - size = receiveOnlyChangedSize(t, m, "ro") + size = mustV(m.ReceiveOnlySize("ro")) if size.Deleted != 0 { t.Fatalf("Receive only: expected no deleted: %+v", size) } @@ -335,7 +338,7 @@ func TestRecvOnlyRemoteUndoChanges(t *testing.T) { m, f, wcfgCancel := setupROFolder(t) defer wcfgCancel() - ffs := f.Filesystem(nil) + ffs := f.Filesystem() defer cleanupModel(m) conn := addFakeConn(m, device1, f.ID) @@ -356,19 +359,19 @@ func TestRecvOnlyRemoteUndoChanges(t *testing.T) { // Everything should be in sync. - size := globalSize(t, m, "ro") + size := mustV(m.GlobalSize("ro")) if size.Files != 1 || size.Directories != 1 { t.Fatalf("Global: expected 1 file and 1 directory: %+v", size) } - size = localSize(t, m, "ro") + size = mustV(m.LocalSize("ro", protocol.LocalDeviceID)) if size.Files != 1 || size.Directories != 1 { t.Fatalf("Local: expected 1 file and 1 directory: %+v", size) } - size = needSizeLocal(t, m, "ro") + size = mustV(m.NeedSize("ro", protocol.LocalDeviceID)) if size.Files+size.Directories > 0 { t.Fatalf("Need: expected nothing: %+v", size) } - size = receiveOnlyChangedSize(t, m, "ro") + size = mustV(m.ReceiveOnlySize("ro")) if size.Files+size.Directories > 0 { t.Fatalf("ROChanged: expected nothing: %+v", size) } @@ -382,7 +385,7 @@ func TestRecvOnlyRemoteUndoChanges(t *testing.T) { must(t, m.ScanFolder("ro")) - size = receiveOnlyChangedSize(t, m, "ro") + size = mustV(m.ReceiveOnlySize("ro")) if size.Files != 2 { t.Fatalf("Receive only: expected 2 files: %+v", size) } @@ -390,17 +393,17 @@ func TestRecvOnlyRemoteUndoChanges(t *testing.T) { // Do the same changes on the remote files := make([]protocol.FileInfo, 0, 2) - snap := fsetSnapshot(t, f.fset) - snap.WithHave(protocol.LocalDeviceID, func(f protocol.FileInfo) bool { + for f, err := range itererr.Zip(f.db.AllLocalFiles("ro", protocol.LocalDeviceID)) { + if err != nil { + t.Fatal(err) + } if f.Name != file && f.Name != knownFile { - return true + continue } f.LocalFlags = 0 f.Version = protocol.Vector{}.Update(device1.Short()) files = append(files, f) - return true - }) - snap.Release() + } must(t, m.IndexUpdate(conn, &protocol.IndexUpdate{Folder: "ro", Files: files})) // Ensure the pull to resolve conflicts (content identical) happened @@ -409,7 +412,10 @@ func TestRecvOnlyRemoteUndoChanges(t *testing.T) { return nil })) - size = receiveOnlyChangedSize(t, m, "ro") + size, err := m.ReceiveOnlySize("ro") + if err != nil { + t.Fatal(err) + } if size.Files+size.Directories+size.Deleted != 0 { t.Fatalf("Receive only: expected all zero: %+v", size) } @@ -424,7 +430,7 @@ func TestRecvOnlyRevertOwnID(t *testing.T) { m, f, wcfgCancel := setupROFolder(t) defer wcfgCancel() - ffs := f.Filesystem(nil) + ffs := f.Filesystem() defer cleanupModel(m) conn := addFakeConn(m, device1, f.ID) @@ -484,7 +490,7 @@ func TestRecvOnlyLocalChangeDoesNotCauseConflict(t *testing.T) { m, f, wcfgCancel := setupROFolder(t) defer wcfgCancel() - ffs := f.Filesystem(nil) + ffs := f.Filesystem() defer cleanupModel(m) conn := addFakeConn(m, device1, f.ID) @@ -505,19 +511,19 @@ func TestRecvOnlyLocalChangeDoesNotCauseConflict(t *testing.T) { // Everything should be in sync. - size := globalSize(t, m, "ro") + size := mustV(m.GlobalSize("ro")) if size.Files != 1 || size.Directories != 1 { t.Fatalf("Global: expected 1 file and 1 directory: %+v", size) } - size = localSize(t, m, "ro") + size = mustV(m.LocalSize("ro", protocol.LocalDeviceID)) if size.Files != 1 || size.Directories != 1 { t.Fatalf("Local: expected 1 file and 1 directory: %+v", size) } - size = needSizeLocal(t, m, "ro") + size = mustV(m.NeedSize("ro", protocol.LocalDeviceID)) if size.Files+size.Directories > 0 { t.Fatalf("Need: expected nothing: %+v", size) } - size = receiveOnlyChangedSize(t, m, "ro") + size = mustV(m.ReceiveOnlySize("ro")) if size.Files+size.Directories > 0 { t.Fatalf("ROChanged: expected nothing: %+v", size) } @@ -528,7 +534,7 @@ func TestRecvOnlyLocalChangeDoesNotCauseConflict(t *testing.T) { must(t, m.ScanFolder("ro")) - size = receiveOnlyChangedSize(t, m, "ro") + size = mustV(m.ReceiveOnlySize("ro")) if size.Files != 1 { t.Fatalf("Receive only: expected 1 file: %+v", size) } @@ -541,7 +547,7 @@ func TestRecvOnlyLocalChangeDoesNotCauseConflict(t *testing.T) { must(t, m.ScanFolder("ro")) - size = needSizeLocal(t, m, "ro") + size = mustV(m.NeedSize("ro", protocol.LocalDeviceID)) if size.Files != 0 { t.Fatalf("Need: expected nothing: %+v", size) } @@ -560,7 +566,7 @@ func setupKnownFiles(t *testing.T, ffs fs.Filesystem, data []byte) []protocol.Fi if err != nil { t.Fatal(err) } - blocks, _ := scanner.Blocks(context.TODO(), bytes.NewReader(data), protocol.BlockSize(int64(len(data))), int64(len(data)), nil, true) + blocks, _ := scanner.Blocks(context.TODO(), bytes.NewReader(data), protocol.BlockSize(int64(len(data))), int64(len(data)), nil) knownFiles := []protocol.FileInfo{ { Name: "knownDir", @@ -577,7 +583,7 @@ func setupKnownFiles(t *testing.T, ffs fs.Filesystem, data []byte) []protocol.Fi ModifiedS: fi.ModTime().Unix(), ModifiedNs: int32(fi.ModTime().Nanosecond()), Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 42}}}, - Sequence: 42, + Sequence: 43, Blocks: blocks, }, } diff --git a/lib/model/folder_sendonly.go b/lib/model/folder_sendonly.go index 4d5d36b19..1273be598 100644 --- a/lib/model/folder_sendonly.go +++ b/lib/model/folder_sendonly.go @@ -7,8 +7,8 @@ package model import ( + "github.com/syncthing/syncthing/internal/itererr" "github.com/syncthing/syncthing/lib/config" - "github.com/syncthing/syncthing/lib/db" "github.com/syncthing/syncthing/lib/events" "github.com/syncthing/syncthing/lib/ignore" "github.com/syncthing/syncthing/lib/protocol" @@ -24,11 +24,11 @@ type sendOnlyFolder struct { folder } -func newSendOnlyFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, _ versioner.Versioner, evLogger events.Logger, ioLimiter *semaphore.Semaphore) service { +func newSendOnlyFolder(model *model, ignores *ignore.Matcher, cfg config.FolderConfiguration, _ versioner.Versioner, evLogger events.Logger, ioLimiter *semaphore.Semaphore) service { f := &sendOnlyFolder{ - folder: newFolder(model, fset, ignores, cfg, evLogger, ioLimiter, nil), + folder: newFolder(model, ignores, cfg, evLogger, ioLimiter, nil), } - f.folder.puller = f + f.puller = f return f } @@ -38,36 +38,36 @@ func (*sendOnlyFolder) PullErrors() []FileError { // pull checks need for files that only differ by metadata (no changes on disk) func (f *sendOnlyFolder) pull() (bool, error) { - batch := db.NewFileInfoBatch(func(files []protocol.FileInfo) error { + batch := NewFileInfoBatch(func(files []protocol.FileInfo) error { f.updateLocalsFromPulling(files) return nil }) - snap, err := f.dbSnapshot() - if err != nil { - return false, err - } - defer snap.Release() - snap.WithNeed(protocol.LocalDeviceID, func(file protocol.FileInfo) bool { - batch.FlushIfFull() + for file, err := range itererr.Zip(f.db.AllNeededGlobalFiles(f.folderID, protocol.LocalDeviceID, config.PullOrderAlphabetic, 0, 0)) { + if err != nil { + return false, err + } + if err := batch.FlushIfFull(); err != nil { + return false, err + } if f.ignores.Match(file.FileName()).IsIgnored() { file.SetIgnored() batch.Append(file) l.Debugln(f, "Handling ignored file", file) - return true + continue } - curFile, ok := snap.Get(protocol.LocalDeviceID, file.FileName()) + curFile, ok, err := f.db.GetDeviceFile(f.folderID, protocol.LocalDeviceID, file.FileName()) + if err != nil { + return false, err + } if !ok { - if file.IsInvalid() { - // Global invalid file just exists for need accounting + if file.IsInvalid() || file.IsDeleted() { + // Accept the file for accounting purposes batch.Append(file) - } else if file.IsDeleted() { - l.Debugln("Should never get a deleted file as needed when we don't have it") - f.evLogger.Log(events.Failure, "got deleted file that doesn't exist locally as needed when pulling on send-only") } - return true + continue } if !file.IsEquivalentOptional(curFile, protocol.FileInfoComparison{ @@ -76,14 +76,12 @@ func (f *sendOnlyFolder) pull() (bool, error) { IgnoreOwnership: !f.SyncOwnership, IgnoreXattrs: !f.SyncXattrs, }) { - return true + continue } batch.Append(file) l.Debugln(f, "Merging versions of identical file", file) - - return true - }) + } batch.Flush() @@ -100,25 +98,31 @@ func (f *sendOnlyFolder) override() error { f.setState(FolderScanning) defer f.setState(FolderIdle) - batch := db.NewFileInfoBatch(func(files []protocol.FileInfo) error { + batch := NewFileInfoBatch(func(files []protocol.FileInfo) error { f.updateLocalsFromScanning(files) return nil }) - snap, err := f.dbSnapshot() - if err != nil { - return err - } - defer snap.Release() - snap.WithNeed(protocol.LocalDeviceID, func(need protocol.FileInfo) bool { - _ = batch.FlushIfFull() - have, ok := snap.Get(protocol.LocalDeviceID, need.Name) + for need, err := range itererr.Zip(f.db.AllNeededGlobalFiles(f.folderID, protocol.LocalDeviceID, config.PullOrderAlphabetic, 0, 0)) { + if err != nil { + return err + } + if err := batch.FlushIfFull(); err != nil { + return err + } + + have, haveOk, err := f.db.GetDeviceFile(f.folderID, protocol.LocalDeviceID, need.Name) + if err != nil { + return err + } + // Don't override files that are in a bad state (ignored, // unsupported, must rescan, ...). - if ok && have.IsInvalid() { - return true + if haveOk && have.IsInvalid() { + continue } - if !ok || have.Name != need.Name { + + if !haveOk || have.Name != need.Name { // We are missing the file need.SetDeleted(f.shortID) } else { @@ -128,7 +132,6 @@ func (f *sendOnlyFolder) override() error { } need.Sequence = 0 batch.Append(need) - return true - }) + } return batch.Flush() } diff --git a/lib/model/folder_sendrecv.go b/lib/model/folder_sendrecv.go index 790a6b734..ac84fd832 100644 --- a/lib/model/folder_sendrecv.go +++ b/lib/model/folder_sendrecv.go @@ -19,9 +19,9 @@ import ( "strings" "time" + "github.com/syncthing/syncthing/internal/itererr" "github.com/syncthing/syncthing/lib/build" "github.com/syncthing/syncthing/lib/config" - "github.com/syncthing/syncthing/lib/db" "github.com/syncthing/syncthing/lib/events" "github.com/syncthing/syncthing/lib/fs" "github.com/syncthing/syncthing/lib/ignore" @@ -31,7 +31,6 @@ import ( "github.com/syncthing/syncthing/lib/semaphore" "github.com/syncthing/syncthing/lib/sync" "github.com/syncthing/syncthing/lib/versioner" - "github.com/syncthing/syncthing/lib/weakhash" ) var ( @@ -130,14 +129,14 @@ type sendReceiveFolder struct { tempPullErrors map[string]string // pull errors that might be just transient } -func newSendReceiveFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, evLogger events.Logger, ioLimiter *semaphore.Semaphore) service { +func newSendReceiveFolder(model *model, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, evLogger events.Logger, ioLimiter *semaphore.Semaphore) service { f := &sendReceiveFolder{ - folder: newFolder(model, fset, ignores, cfg, evLogger, ioLimiter, ver), + folder: newFolder(model, ignores, cfg, evLogger, ioLimiter, ver), queue: newJobQueue(), blockPullReorderer: newBlockPullReorderer(cfg.BlockPullOrder, model.id, cfg.DeviceIDs()), writeLimiter: semaphore.New(cfg.MaxConcurrentWrites), } - f.folder.puller = f + f.puller = f if f.Copiers == 0 { f.Copiers = defaultCopiers @@ -180,7 +179,7 @@ func (f *sendReceiveFolder) pull() (bool, error) { f.errorsMut.Unlock() var err error - for tries := 0; tries < maxPullerIterations; tries++ { + for tries := range maxPullerIterations { select { case <-f.ctx.Done(): return false, f.ctx.Err() @@ -241,12 +240,6 @@ func (f *sendReceiveFolder) pullerIteration(scanChan chan<- string) (int, error) f.tempPullErrors = make(map[string]string) f.errorsMut.Unlock() - snap, err := f.dbSnapshot() - if err != nil { - return 0, err - } - defer snap.Release() - pullChan := make(chan pullBlockState) copyChan := make(chan copyBlocksState) finisherChan := make(chan *sharedPullerState) @@ -266,7 +259,7 @@ func (f *sendReceiveFolder) pullerIteration(scanChan chan<- string) (int, error) updateWg.Done() }() - for i := 0; i < f.Copiers; i++ { + for range f.Copiers { copyWg.Add(1) go func() { // copierRoutine finishes when copyChan is closed @@ -278,18 +271,18 @@ func (f *sendReceiveFolder) pullerIteration(scanChan chan<- string) (int, error) pullWg.Add(1) go func() { // pullerRoutine finishes when pullChan is closed - f.pullerRoutine(snap, pullChan, finisherChan) + f.pullerRoutine(pullChan, finisherChan) pullWg.Done() }() doneWg.Add(1) // finisherRoutine finishes when finisherChan is closed go func() { - f.finisherRoutine(snap, finisherChan, dbUpdateChan, scanChan) + f.finisherRoutine(finisherChan, dbUpdateChan, scanChan) doneWg.Done() }() - changed, fileDeletions, dirDeletions, err := f.processNeeded(snap, dbUpdateChan, copyChan, scanChan) + changed, fileDeletions, dirDeletions, err := f.processNeeded(dbUpdateChan, copyChan, scanChan) // Signal copy and puller routines that we are done with the in data for // this iteration. Wait for them to finish. @@ -304,7 +297,7 @@ func (f *sendReceiveFolder) pullerIteration(scanChan chan<- string) (int, error) doneWg.Wait() if err == nil { - f.processDeletions(fileDeletions, dirDeletions, snap, dbUpdateChan, scanChan) + f.processDeletions(fileDeletions, dirDeletions, dbUpdateChan, scanChan) } // Wait for db updates and scan scheduling to complete @@ -316,7 +309,7 @@ func (f *sendReceiveFolder) pullerIteration(scanChan chan<- string) (int, error) return changed, err } -func (f *sendReceiveFolder) processNeeded(snap *db.Snapshot, dbUpdateChan chan<- dbUpdateJob, copyChan chan<- copyBlocksState, scanChan chan<- string) (int, map[string]protocol.FileInfo, []protocol.FileInfo, error) { +func (f *sendReceiveFolder) processNeeded(dbUpdateChan chan<- dbUpdateJob, copyChan chan<- copyBlocksState, scanChan chan<- string) (int, map[string]protocol.FileInfo, []protocol.FileInfo, error) { changed := 0 var dirDeletions []protocol.FileInfo fileDeletions := map[string]protocol.FileInfo{} @@ -326,16 +319,20 @@ func (f *sendReceiveFolder) processNeeded(snap *db.Snapshot, dbUpdateChan chan<- // Regular files to pull goes into the file queue, everything else // (directories, symlinks and deletes) goes into the "process directly" // pile. - snap.WithNeed(protocol.LocalDeviceID, func(file protocol.FileInfo) bool { +loop: + for file, err := range itererr.Zip(f.model.sdb.AllNeededGlobalFiles(f.folderID, protocol.LocalDeviceID, f.Order, 0, 0)) { + if err != nil { + return changed, nil, nil, err + } select { case <-f.ctx.Done(): - return false + break loop default: } if f.IgnoreDelete && file.IsDeleted() { l.Debugln(f, "ignore file deletion (config)", file.FileName()) - return true + continue } changed++ @@ -362,14 +359,18 @@ func (f *sendReceiveFolder) processNeeded(snap *db.Snapshot, dbUpdateChan chan<- } case file.IsDeleted(): - if file.IsDirectory() { + switch { + case file.IsDirectory(): // Perform directory deletions at the end, as we may have // files to delete inside them before we get to that point. dirDeletions = append(dirDeletions, file) - } else if file.IsSymlink() { - f.deleteFile(file, snap, dbUpdateChan, scanChan) - } else { - df, ok := snap.Get(protocol.LocalDeviceID, file.Name) + case file.IsSymlink(): + f.deleteFile(file, dbUpdateChan, scanChan) + default: + df, ok, err := f.model.sdb.GetDeviceFile(f.folderID, protocol.LocalDeviceID, file.Name) + if err != nil { + return changed, nil, nil, err + } // Local file can be already deleted, but with a lower version // number, hence the deletion coming in again as part of // WithNeed, furthermore, the file can simply be of the wrong @@ -385,7 +386,10 @@ func (f *sendReceiveFolder) processNeeded(snap *db.Snapshot, dbUpdateChan chan<- } case file.Type == protocol.FileInfoTypeFile: - curFile, hasCurFile := snap.Get(protocol.LocalDeviceID, file.Name) + curFile, hasCurFile, err := f.model.sdb.GetDeviceFile(f.folderID, protocol.LocalDeviceID, file.Name) + if err != nil { + return changed, nil, nil, err + } if hasCurFile && file.BlocksEqual(curFile) { // We are supposed to copy the entire file, and then fetch nothing. We // are only updating metadata, so we don't actually *need* to make the @@ -397,7 +401,7 @@ func (f *sendReceiveFolder) processNeeded(snap *db.Snapshot, dbUpdateChan chan<- } case (build.IsWindows || build.IsAndroid) && file.IsSymlink(): - if err := f.handleSymlinkCheckExisting(file, snap, scanChan); err != nil { + if err := f.handleSymlinkCheckExisting(file, scanChan); err != nil { f.newPullError(file.Name, fmt.Errorf("handling unsupported symlink: %w", err)) break } @@ -408,22 +412,20 @@ func (f *sendReceiveFolder) processNeeded(snap *db.Snapshot, dbUpdateChan chan<- case file.IsDirectory() && !file.IsSymlink(): l.Debugln(f, "Handling directory", file.Name) if f.checkParent(file.Name, scanChan) { - f.handleDir(file, snap, dbUpdateChan, scanChan) + f.handleDir(file, dbUpdateChan, scanChan) } case file.IsSymlink(): l.Debugln(f, "Handling symlink", file.Name) if f.checkParent(file.Name, scanChan) { - f.handleSymlink(file, snap, dbUpdateChan, scanChan) + f.handleSymlink(file, dbUpdateChan, scanChan) } default: l.Warnln(file) panic("unhandleable item type, can't happen") } - - return true - }) + } select { case <-f.ctx.Done(): @@ -431,23 +433,6 @@ func (f *sendReceiveFolder) processNeeded(snap *db.Snapshot, dbUpdateChan chan<- default: } - // Now do the file queue. Reorder it according to configuration. - - switch f.Order { - case config.PullOrderRandom: - f.queue.Shuffle() - case config.PullOrderAlphabetic: - // The queue is already in alphabetic order. - case config.PullOrderSmallestFirst: - f.queue.SortSmallestFirst() - case config.PullOrderLargestFirst: - f.queue.SortLargestFirst() - case config.PullOrderOldestFirst: - f.queue.SortOldestFirst() - case config.PullOrderNewestFirst: - f.queue.SortNewestFirst() - } - // Process the file queue. nextFile: @@ -463,7 +448,10 @@ nextFile: break } - fi, ok := snap.GetGlobal(fileName) + fi, ok, err := f.model.sdb.GetGlobalFile(f.folderID, fileName) + if err != nil { + return changed, nil, nil, err + } if !ok { // File is no longer in the index. Mark it as done and drop it. f.queue.Done(fileName) @@ -490,7 +478,7 @@ nextFile: // desired state with the delete bit set is in the deletion // map. desired := fileDeletions[candidate.Name] - if err := f.renameFile(candidate, desired, fi, snap, dbUpdateChan, scanChan); err != nil { + if err := f.renameFile(candidate, desired, fi, dbUpdateChan, scanChan); err != nil { l.Debugf("rename shortcut for %s failed: %s", fi.Name, err.Error()) // Failed to rename, try next one. continue @@ -503,9 +491,11 @@ nextFile: continue nextFile } - devices := f.model.fileAvailability(f.FolderConfiguration, snap, fi) + devices := f.model.fileAvailability(f.FolderConfiguration, fi) if len(devices) > 0 { - f.handleFile(fi, snap, copyChan) + if err := f.handleFile(fi, copyChan); err != nil { + f.newPullError(fileName, err) + } continue } f.newPullError(fileName, errNotAvailable) @@ -525,7 +515,7 @@ func popCandidate(buckets map[string][]protocol.FileInfo, key string) (protocol. return cands[0], true } -func (f *sendReceiveFolder) processDeletions(fileDeletions map[string]protocol.FileInfo, dirDeletions []protocol.FileInfo, snap *db.Snapshot, dbUpdateChan chan<- dbUpdateJob, scanChan chan<- string) { +func (f *sendReceiveFolder) processDeletions(fileDeletions map[string]protocol.FileInfo, dirDeletions []protocol.FileInfo, dbUpdateChan chan<- dbUpdateJob, scanChan chan<- string) { for _, file := range fileDeletions { select { case <-f.ctx.Done(): @@ -533,7 +523,7 @@ func (f *sendReceiveFolder) processDeletions(fileDeletions map[string]protocol.F default: } - f.deleteFile(file, snap, dbUpdateChan, scanChan) + f.deleteFile(file, dbUpdateChan, scanChan) } // Process in reverse order to delete depth first @@ -546,12 +536,12 @@ func (f *sendReceiveFolder) processDeletions(fileDeletions map[string]protocol.F dir := dirDeletions[len(dirDeletions)-i-1] l.Debugln(f, "Deleting dir", dir.Name) - f.deleteDir(dir, snap, dbUpdateChan, scanChan) + f.deleteDir(dir, dbUpdateChan, scanChan) } } // handleDir creates or updates the given directory -func (f *sendReceiveFolder) handleDir(file protocol.FileInfo, snap *db.Snapshot, dbUpdateChan chan<- dbUpdateJob, scanChan chan<- string) { +func (f *sendReceiveFolder) handleDir(file protocol.FileInfo, dbUpdateChan chan<- dbUpdateJob, scanChan chan<- string) { // Used in the defer closure below, updated by the function body. Take // care not declare another err. var err error @@ -579,7 +569,7 @@ func (f *sendReceiveFolder) handleDir(file protocol.FileInfo, snap *db.Snapshot, } if shouldDebug() { - curFile, _ := snap.Get(protocol.LocalDeviceID, file.Name) + curFile, _, _ := f.model.sdb.GetDeviceFile(f.folderID, protocol.LocalDeviceID, file.Name) l.Debugf("need dir\n\t%v\n\t%v", file, curFile) } @@ -590,7 +580,11 @@ func (f *sendReceiveFolder) handleDir(file protocol.FileInfo, snap *db.Snapshot, // that don't result in a conflict. case err == nil && !info.IsDir(): // Check that it is what we have in the database. - curFile, hasCurFile := snap.Get(protocol.LocalDeviceID, file.Name) + curFile, hasCurFile, err := f.model.sdb.GetDeviceFile(f.folderID, protocol.LocalDeviceID, file.Name) + if err != nil { + f.newPullError(file.Name, fmt.Errorf("handling dir: %w", err)) + return + } if err := f.scanIfItemChanged(file.Name, info, curFile, hasCurFile, false, scanChan); err != nil { f.newPullError(file.Name, fmt.Errorf("handling dir: %w", err)) return @@ -607,7 +601,7 @@ func (f *sendReceiveFolder) handleDir(file protocol.FileInfo, snap *db.Snapshot, return f.moveForConflict(name, file.ModifiedBy.String(), scanChan) }, curFile.Name) } else { - err = f.deleteItemOnDisk(curFile, snap, scanChan) + err = f.deleteItemOnDisk(curFile, scanChan) } if err != nil { f.newPullError(file.Name, err) @@ -716,7 +710,7 @@ func (f *sendReceiveFolder) checkParent(file string, scanChan chan<- string) boo } // handleSymlink creates or updates the given symlink -func (f *sendReceiveFolder) handleSymlink(file protocol.FileInfo, snap *db.Snapshot, dbUpdateChan chan<- dbUpdateJob, scanChan chan<- string) { +func (f *sendReceiveFolder) handleSymlink(file protocol.FileInfo, dbUpdateChan chan<- dbUpdateJob, scanChan chan<- string) { // Used in the defer closure below, updated by the function body. Take // care not declare another err. var err error @@ -739,8 +733,8 @@ func (f *sendReceiveFolder) handleSymlink(file protocol.FileInfo, snap *db.Snaps }() if shouldDebug() { - curFile, _ := snap.Get(protocol.LocalDeviceID, file.Name) - l.Debugf("need symlink\n\t%v\n\t%v", file, curFile) + curFile, ok, _ := f.model.sdb.GetDeviceFile(f.folderID, protocol.LocalDeviceID, file.Name) + l.Debugf("need symlink\n\t%v\n\t%v", file, curFile, ok) } if len(file.SymlinkTarget) == 0 { @@ -750,7 +744,7 @@ func (f *sendReceiveFolder) handleSymlink(file protocol.FileInfo, snap *db.Snaps return } - if err = f.handleSymlinkCheckExisting(file, snap, scanChan); err != nil { + if err = f.handleSymlinkCheckExisting(file, scanChan); err != nil { f.newPullError(file.Name, fmt.Errorf("handling symlink: %w", err)) return } @@ -771,7 +765,7 @@ func (f *sendReceiveFolder) handleSymlink(file protocol.FileInfo, snap *db.Snaps } } -func (f *sendReceiveFolder) handleSymlinkCheckExisting(file protocol.FileInfo, snap *db.Snapshot, scanChan chan<- string) error { +func (f *sendReceiveFolder) handleSymlinkCheckExisting(file protocol.FileInfo, scanChan chan<- string) error { // If there is already something under that name, we need to handle that. info, err := f.mtimefs.Lstat(file.Name) if err != nil { @@ -781,7 +775,10 @@ func (f *sendReceiveFolder) handleSymlinkCheckExisting(file protocol.FileInfo, s return err } // Check that it is what we have in the database. - curFile, hasCurFile := snap.Get(protocol.LocalDeviceID, file.Name) + curFile, hasCurFile, err := f.model.sdb.GetDeviceFile(f.folderID, protocol.LocalDeviceID, file.Name) + if err != nil { + return err + } if err := f.scanIfItemChanged(file.Name, info, curFile, hasCurFile, false, scanChan); err != nil { return err } @@ -797,12 +794,12 @@ func (f *sendReceiveFolder) handleSymlinkCheckExisting(file protocol.FileInfo, s return f.moveForConflict(name, file.ModifiedBy.String(), scanChan) }, curFile.Name) } else { - return f.deleteItemOnDisk(curFile, snap, scanChan) + return f.deleteItemOnDisk(curFile, scanChan) } } // deleteDir attempts to remove a directory that was deleted on a remote -func (f *sendReceiveFolder) deleteDir(file protocol.FileInfo, snap *db.Snapshot, dbUpdateChan chan<- dbUpdateJob, scanChan chan<- string) { +func (f *sendReceiveFolder) deleteDir(file protocol.FileInfo, dbUpdateChan chan<- dbUpdateJob, scanChan chan<- string) { // Used in the defer closure below, updated by the function body. Take // care not declare another err. var err error @@ -827,7 +824,10 @@ func (f *sendReceiveFolder) deleteDir(file protocol.FileInfo, snap *db.Snapshot, }) }() - cur, hasCur := snap.Get(protocol.LocalDeviceID, file.Name) + cur, hasCur, err := f.model.sdb.GetDeviceFile(f.folderID, protocol.LocalDeviceID, file.Name) + if err != nil { + return + } if err = f.checkToBeDeleted(file, cur, hasCur, scanChan); err != nil { if fs.IsNotExist(err) || fs.IsErrCaseConflict(err) { @@ -837,7 +837,7 @@ func (f *sendReceiveFolder) deleteDir(file protocol.FileInfo, snap *db.Snapshot, return } - if err = f.deleteDirOnDisk(file.Name, snap, scanChan); err != nil { + if err = f.deleteDirOnDisk(file.Name, scanChan); err != nil { return } @@ -845,8 +845,12 @@ func (f *sendReceiveFolder) deleteDir(file protocol.FileInfo, snap *db.Snapshot, } // deleteFile attempts to delete the given file -func (f *sendReceiveFolder) deleteFile(file protocol.FileInfo, snap *db.Snapshot, dbUpdateChan chan<- dbUpdateJob, scanChan chan<- string) { - cur, hasCur := snap.Get(protocol.LocalDeviceID, file.Name) +func (f *sendReceiveFolder) deleteFile(file protocol.FileInfo, dbUpdateChan chan<- dbUpdateJob, scanChan chan<- string) { + cur, hasCur, err := f.model.sdb.GetDeviceFile(f.folderID, protocol.LocalDeviceID, file.Name) + if err != nil { + f.newPullError(file.Name, fmt.Errorf("delete file: %w", err)) + return + } f.deleteFileWithCurrent(file, cur, hasCur, dbUpdateChan, scanChan) } @@ -925,7 +929,7 @@ func (f *sendReceiveFolder) deleteFileWithCurrent(file, cur protocol.FileInfo, h // renameFile attempts to rename an existing file to a destination // and set the right attributes on it. -func (f *sendReceiveFolder) renameFile(cur, source, target protocol.FileInfo, snap *db.Snapshot, dbUpdateChan chan<- dbUpdateJob, scanChan chan<- string) error { +func (f *sendReceiveFolder) renameFile(cur, source, target protocol.FileInfo, dbUpdateChan chan<- dbUpdateJob, scanChan chan<- string) error { // Used in the defer closure below, updated by the function body. Take // care not declare another err. var err error @@ -967,10 +971,13 @@ func (f *sendReceiveFolder) renameFile(cur, source, target protocol.FileInfo, sn return err } // Check that the target corresponds to what we have in the DB - curTarget, ok := snap.Get(protocol.LocalDeviceID, target.Name) + curTarget, ok, err := f.model.sdb.GetDeviceFile(f.folderID, protocol.LocalDeviceID, target.Name) + if err != nil { + return err + } switch stat, serr := f.mtimefs.Lstat(target.Name); { case serr != nil: - var caseErr *fs.ErrCaseConflict + var caseErr *fs.CaseConflictError switch { case errors.As(serr, &caseErr): if caseErr.Real != source.Name { @@ -1017,7 +1024,7 @@ func (f *sendReceiveFolder) renameFile(cur, source, target protocol.FileInfo, sn tempName := fs.TempName(target.Name) if f.versioner != nil { - err = f.CheckAvailableSpace(uint64(source.Size)) + err = f.CheckAvailableSpace(uint64(source.Size)) //nolint:gosec if err == nil { err = osutil.Copy(f.CopyRangeMethod.ToFS(), f.mtimefs, f.mtimefs, source.Name, tempName) if err == nil { @@ -1041,7 +1048,7 @@ func (f *sendReceiveFolder) renameFile(cur, source, target protocol.FileInfo, sn // of the source and the creation of the target temp file. Fix-up the metadata, // update the local index of the target file and rename from temp to real name. - if err = f.performFinish(target, curTarget, true, tempName, snap, dbUpdateChan, scanChan); err != nil { + if err = f.performFinish(target, curTarget, true, tempName, dbUpdateChan, scanChan); err != nil { return err } @@ -1086,8 +1093,11 @@ func (f *sendReceiveFolder) renameFile(cur, source, target protocol.FileInfo, sn // handleFile queues the copies and pulls as necessary for a single new or // changed file. -func (f *sendReceiveFolder) handleFile(file protocol.FileInfo, snap *db.Snapshot, copyChan chan<- copyBlocksState) { - curFile, hasCurFile := snap.Get(protocol.LocalDeviceID, file.Name) +func (f *sendReceiveFolder) handleFile(file protocol.FileInfo, copyChan chan<- copyBlocksState) error { + curFile, hasCurFile, err := f.model.sdb.GetDeviceFile(f.folderID, protocol.LocalDeviceID, file.Name) + if err != nil { + return err + } have, _ := blockDiff(curFile.Blocks, file.Blocks) @@ -1131,17 +1141,18 @@ func (f *sendReceiveFolder) handleFile(file protocol.FileInfo, snap *db.Snapshot have: len(have), } copyChan <- cs + return nil } func (f *sendReceiveFolder) reuseBlocks(blocks []protocol.BlockInfo, reused []int, file protocol.FileInfo, tempName string) ([]protocol.BlockInfo, []int) { // Check for an old temporary file which might have some blocks we could // reuse. - tempBlocks, err := scanner.HashFile(f.ctx, f.ID, f.mtimefs, tempName, file.BlockSize(), nil, false) + tempBlocks, err := scanner.HashFile(f.ctx, f.ID, f.mtimefs, tempName, file.BlockSize(), nil) if err != nil { - var caseErr *fs.ErrCaseConflict + var caseErr *fs.CaseConflictError if errors.As(err, &caseErr) { if rerr := f.mtimefs.Rename(caseErr.Real, tempName); rerr == nil { - tempBlocks, err = scanner.HashFile(f.ctx, f.ID, f.mtimefs, tempName, file.BlockSize(), nil, false) + tempBlocks, err = scanner.HashFile(f.ctx, f.ID, f.mtimefs, tempName, file.BlockSize(), nil) } } } @@ -1276,42 +1287,26 @@ func (f *sendReceiveFolder) shortcutFile(file protocol.FileInfo, dbUpdateChan ch // copierRoutine reads copierStates until the in channel closes and performs // the relevant copies when possible, or passes it to the puller routine. func (f *sendReceiveFolder) copierRoutine(in <-chan copyBlocksState, pullChan chan<- pullBlockState, out chan<- *sharedPullerState) { - buf := protocol.BufferPool.Get(protocol.MinBlockSize) - defer func() { - protocol.BufferPool.Put(buf) - }() - - folderFilesystems := make(map[string]fs.Filesystem) - // Hope that it's usually in the same folder, so start with that one. - folders := []string{f.folderID} + otherFolderFilesystems := make(map[string]fs.Filesystem) for folder, cfg := range f.model.cfg.Folders() { - folderFilesystems[folder] = cfg.Filesystem(nil) - if folder != f.folderID { - folders = append(folders, folder) + if folder == f.ID { + continue } + otherFolderFilesystems[folder] = cfg.Filesystem() } for state := range in { - if err := f.CheckAvailableSpace(uint64(state.file.Size)); err != nil { + if err := f.CheckAvailableSpace(uint64(state.file.Size)); err != nil { //nolint:gosec state.fail(err) // Nothing more to do for this failed file, since it would use to much disk space out <- state.sharedPullerState continue } - dstFd, err := state.tempFile() - if err != nil { - // Nothing more to do for this failed file, since we couldn't create a temporary for it. - out <- state.sharedPullerState - continue - } - if f.Type != config.FolderTypeReceiveEncrypted { f.model.progressEmitter.Register(state.sharedPullerState) } - weakHashFinder, file := f.initWeakHashFinder(state) - blocks: for _, block := range state.blocks { select { @@ -1334,147 +1329,140 @@ func (f *sendReceiveFolder) copierRoutine(in <-chan copyBlocksState, pullChan ch continue } - buf = protocol.BufferPool.Upgrade(buf, int(block.Size)) - - var found bool - if f.Type != config.FolderTypeReceiveEncrypted { - found, err = weakHashFinder.Iterate(block.WeakHash, buf, func(offset int64) bool { - if f.verifyBuffer(buf, block) != nil { - return true - } - - err = f.limitedWriteAt(dstFd, buf, block.Offset) - if err != nil { - state.fail(fmt.Errorf("dst write: %w", err)) - } - if offset == block.Offset { - state.copiedFromOrigin(block.Size) - } else { - state.copiedFromOriginShifted(block.Size) - } - - return false - }) - if err != nil { - l.Debugln("weak hasher iter", err) - } - } - - if !found { - found = f.model.finder.Iterate(folders, block.Hash, func(folder, path string, index int32) bool { - ffs := folderFilesystems[folder] - fd, err := ffs.Open(path) - if err != nil { - return false - } - defer fd.Close() - - srcOffset := int64(state.file.BlockSize()) * int64(index) - _, err = fd.ReadAt(buf, srcOffset) - if err != nil { - return false - } - - // Hash is not SHA256 as it's an encrypted hash token. In that - // case we can't verify the block integrity so we'll take it on - // trust. (The other side can and will verify.) - if f.Type != config.FolderTypeReceiveEncrypted { - if err := f.verifyBuffer(buf, block); err != nil { - l.Debugln("Finder failed to verify buffer", err) - return false - } - } - - if f.CopyRangeMethod != config.CopyRangeMethodStandard { - err = f.withLimiter(func() error { - dstFd.mut.Lock() - defer dstFd.mut.Unlock() - return fs.CopyRange(f.CopyRangeMethod.ToFS(), fd, dstFd.fd, srcOffset, block.Offset, int64(block.Size)) - }) - } else { - err = f.limitedWriteAt(dstFd, buf, block.Offset) - } - if err != nil { - state.fail(fmt.Errorf("dst write: %w", err)) - } - if path == state.file.Name { - state.copiedFromOrigin(block.Size) - } else { - state.copiedFromElsewhere(block.Size) - } - return true - }) + if f.copyBlock(block, state, otherFolderFilesystems) { + state.copyDone(block) + continue } if state.failed() != nil { break } - if !found { - state.pullStarted() - ps := pullBlockState{ - sharedPullerState: state.sharedPullerState, - block: block, - } - pullChan <- ps - } else { - state.copyDone(block) + state.pullStarted() + ps := pullBlockState{ + sharedPullerState: state.sharedPullerState, + block: block, } + pullChan <- ps } - if file != nil { - // os.File used to return invalid argument if nil. - // fs.File panics as it's an interface. - file.Close() + // If there are no blocks to pull/copy, we still need the temporary file in place. + if len(state.blocks) == 0 { + _, err := state.tempFile() + if err != nil { + state.fail(err) + } } out <- state.sharedPullerState } } -func (f *sendReceiveFolder) initWeakHashFinder(state copyBlocksState) (*weakhash.Finder, fs.File) { - if f.Type == config.FolderTypeReceiveEncrypted { - l.Debugln("not weak hashing due to folder type", f.Type) - return nil, nil +// Returns true when the block was successfully copied. +func (f *sendReceiveFolder) copyBlock(block protocol.BlockInfo, state copyBlocksState, otherFolderFilesystems map[string]fs.Filesystem) bool { + buf := protocol.BufferPool.Get(block.Size) + defer protocol.BufferPool.Put(buf) + + // Hope that it's usually in the same folder, so start with that + // one. Also possibly more efficient copy (same filesystem). + if f.copyBlockFromFolder(f.ID, block, state, f.mtimefs, buf) { + return true + } + if state.failed() != nil { + return false } - blocksPercentChanged := 0 - if tot := len(state.file.Blocks); tot > 0 { - blocksPercentChanged = (tot - state.have) * 100 / tot - } - - if blocksPercentChanged < f.WeakHashThresholdPct { - l.Debugf("not weak hashing %s. not enough changed %.02f < %d", state.file.Name, blocksPercentChanged, f.WeakHashThresholdPct) - return nil, nil - } - - hashesToFind := make([]uint32, 0, len(state.blocks)) - for _, block := range state.blocks { - if block.WeakHash != 0 { - hashesToFind = append(hashesToFind, block.WeakHash) + for folderID, ffs := range otherFolderFilesystems { + if f.copyBlockFromFolder(folderID, block, state, ffs, buf) { + return true + } + if state.failed() != nil { + return false } } - if len(hashesToFind) == 0 { - l.Debugf("not weak hashing %s. file did not contain any weak hashes", state.file.Name) - return nil, nil + return false +} + +// Returns true when the block was successfully copied. +// The passed buffer must be large enough to accommodate the block. +func (f *sendReceiveFolder) copyBlockFromFolder(folderID string, block protocol.BlockInfo, state copyBlocksState, ffs fs.Filesystem, buf []byte) bool { + for e, err := range itererr.Zip(f.model.sdb.AllLocalBlocksWithHash(folderID, block.Hash)) { + if err != nil { + // We just ignore this and continue pulling instead (though + // there's a good chance that will fail too, if the DB is + // unhealthy). + l.Debugf("Failed to get information from DB about block %v in copier (folderID %v, file %v): %v", block.Hash, f.folderID, state.file.Name) + return false + } + + if !f.copyBlockFromFile(e.FileName, e.Offset, state, ffs, block, buf) { + if state.failed() != nil { + return false + } + continue + } + + if e.FileName == state.file.Name { + state.copiedFromOrigin(block.Size) + } else { + state.copiedFromElsewhere(block.Size) + } + return true } - file, err := f.mtimefs.Open(state.file.Name) + return false +} + +// Returns true when the block was successfully copied. +// The passed buffer must be large enough to accommodate the block. +func (f *sendReceiveFolder) copyBlockFromFile(srcName string, srcOffset int64, state copyBlocksState, ffs fs.Filesystem, block protocol.BlockInfo, buf []byte) bool { + fd, err := ffs.Open(srcName) if err != nil { - l.Debugln("weak hasher", err) - return nil, nil + l.Debugf("Failed to open file %v trying to copy block %v (folderID %v): %v", srcName, block.Hash, f.folderID, err) + return false + } + defer fd.Close() + + _, err = fd.ReadAt(buf, srcOffset) + if err != nil { + l.Debugf("Failed to read block from file %v in copier (folderID: %v, hash: %v): %v", srcName, f.folderID, block.Hash, err) + return false } - weakHashFinder, err := weakhash.NewFinder(f.ctx, file, state.file.BlockSize(), hashesToFind) - if err != nil { - l.Debugln("weak hasher", err) - return nil, file + // Hash is not SHA256 as it's an encrypted hash token. In that + // case we can't verify the block integrity so we'll take it on + // trust. (The other side can and will verify.) + if f.Type != config.FolderTypeReceiveEncrypted { + if err := f.verifyBuffer(buf, block); err != nil { + l.Debugf("Failed to verify buffer in copier (folderID: %v): %v", f.folderID, err) + return false + } } - return weakHashFinder, file + + dstFd, err := state.tempFile() + if err != nil { + // State is already marked as failed when an error is returned here. + return false + } + + if f.CopyRangeMethod != config.CopyRangeMethodStandard { + err = f.withLimiter(func() error { + dstFd.mut.Lock() + defer dstFd.mut.Unlock() + return fs.CopyRange(f.CopyRangeMethod.ToFS(), fd, dstFd.fd, srcOffset, block.Offset, int64(block.Size)) + }) + } else { + err = f.limitedWriteAt(dstFd, buf, block.Offset) + } + if err != nil { + state.fail(fmt.Errorf("dst write: %w", err)) + return false + } + return true } func (*sendReceiveFolder) verifyBuffer(buf []byte, block protocol.BlockInfo) error { - if len(buf) != int(block.Size) { + if len(buf) != block.Size { return fmt.Errorf("length mismatch %d != %d", len(buf), block.Size) } @@ -1486,7 +1474,7 @@ func (*sendReceiveFolder) verifyBuffer(buf []byte, block protocol.BlockInfo) err return nil } -func (f *sendReceiveFolder) pullerRoutine(snap *db.Snapshot, in <-chan pullBlockState, out chan<- *sharedPullerState) { +func (f *sendReceiveFolder) pullerRoutine(in <-chan pullBlockState, out chan<- *sharedPullerState) { requestLimiter := semaphore.New(f.PullerMaxPendingKiB * 1024) wg := sync.NewWaitGroup() @@ -1502,8 +1490,7 @@ func (f *sendReceiveFolder) pullerRoutine(snap *db.Snapshot, in <-chan pullBlock // ongoing at any given time, based on the size of the blocks // themselves. - state := state - bytes := int(state.block.Size) + bytes := state.block.Size if err := requestLimiter.TakeWithContext(f.ctx, bytes); err != nil { state.fail(err) @@ -1517,13 +1504,13 @@ func (f *sendReceiveFolder) pullerRoutine(snap *db.Snapshot, in <-chan pullBlock defer wg.Done() defer requestLimiter.Give(bytes) - f.pullBlock(state, snap, out) + f.pullBlock(state, out) }() } wg.Wait() } -func (f *sendReceiveFolder) pullBlock(state pullBlockState, snap *db.Snapshot, out chan<- *sharedPullerState) { +func (f *sendReceiveFolder) pullBlock(state pullBlockState, out chan<- *sharedPullerState) { // Get an fd to the temporary file. Technically we don't need it until // after fetching the block, but if we run into an error here there is // no point in issuing the request to the network. @@ -1542,7 +1529,7 @@ func (f *sendReceiveFolder) pullBlock(state pullBlockState, snap *db.Snapshot, o } var lastError error - candidates := f.model.blockAvailability(f.FolderConfiguration, snap, state.file, state.block) + candidates := f.model.blockAvailability(f.FolderConfiguration, state.file, state.block) loop: for { select { @@ -1574,7 +1561,7 @@ loop: activity.using(selected) var buf []byte blockNo := int(state.block.Offset / int64(state.file.BlockSize())) - buf, lastError = f.model.RequestGlobal(f.ctx, selected.ID, f.folderID, state.file.Name, blockNo, state.block.Offset, int(state.block.Size), state.block.Hash, state.block.WeakHash, selected.FromTemporary) + buf, lastError = f.model.RequestGlobal(f.ctx, selected.ID, f.folderID, state.file.Name, blockNo, state.block.Offset, state.block.Size, state.block.Hash, selected.FromTemporary) activity.done(selected) if lastError != nil { l.Debugln("request:", f.folderID, state.file.Name, state.block.Offset, state.block.Size, selected.ID.Short(), "returned error:", lastError) @@ -1607,7 +1594,7 @@ loop: out <- state.sharedPullerState } -func (f *sendReceiveFolder) performFinish(file, curFile protocol.FileInfo, hasCurFile bool, tempName string, snap *db.Snapshot, dbUpdateChan chan<- dbUpdateJob, scanChan chan<- string) error { +func (f *sendReceiveFolder) performFinish(file, curFile protocol.FileInfo, hasCurFile bool, tempName string, dbUpdateChan chan<- dbUpdateJob, scanChan chan<- string) error { // Set the correct permission bits on the new file if !f.IgnorePerms && !file.NoPermissions { if err := f.mtimefs.Chmod(tempName, fs.FileMode(file.Permissions&0o777)); err != nil { @@ -1638,7 +1625,7 @@ func (f *sendReceiveFolder) performFinish(file, curFile protocol.FileInfo, hasCu return f.moveForConflict(name, file.ModifiedBy.String(), scanChan) }, curFile.Name) } else { - err = f.deleteItemOnDisk(curFile, snap, scanChan) + err = f.deleteItemOnDisk(curFile, scanChan) } if err != nil { return fmt.Errorf("moving for conflict: %w", err) @@ -1661,7 +1648,7 @@ func (f *sendReceiveFolder) performFinish(file, curFile protocol.FileInfo, hasCu return nil } -func (f *sendReceiveFolder) finisherRoutine(snap *db.Snapshot, in <-chan *sharedPullerState, dbUpdateChan chan<- dbUpdateJob, scanChan chan<- string) { +func (f *sendReceiveFolder) finisherRoutine(in <-chan *sharedPullerState, dbUpdateChan chan<- dbUpdateJob, scanChan chan<- string) { for state := range in { if closed, err := state.finalClose(); closed { l.Debugln(f, "closing", state.file.Name) @@ -1669,7 +1656,7 @@ func (f *sendReceiveFolder) finisherRoutine(snap *db.Snapshot, in <-chan *shared f.queue.Done(state.file.Name) if err == nil { - err = f.performFinish(state.file, state.curFile, state.hasCurFile, state.tempName, snap, dbUpdateChan, scanChan) + err = f.performFinish(state.file, state.curFile, state.hasCurFile, state.tempName, dbUpdateChan, scanChan) } if err != nil { @@ -1722,11 +1709,11 @@ func (f *sendReceiveFolder) dbUpdaterRoutine(dbUpdateChan <-chan dbUpdateJob) { var lastFile protocol.FileInfo tick := time.NewTicker(maxBatchTime) defer tick.Stop() - batch := db.NewFileInfoBatch(func(files []protocol.FileInfo) error { + batch := NewFileInfoBatch(func(files []protocol.FileInfo) error { // sync directories for dir := range changedDirs { delete(changedDirs, dir) - if !f.FolderConfiguration.DisableFsync { + if !f.DisableFsync { fd, err := f.mtimefs.Open(dir) if err != nil { l.Debugf("fsync %q failed: %v", dir, err) @@ -1909,7 +1896,7 @@ func (f *sendReceiveFolder) newPullError(path string, err error) { } // deleteItemOnDisk deletes the file represented by old that is about to be replaced by new. -func (f *sendReceiveFolder) deleteItemOnDisk(item protocol.FileInfo, snap *db.Snapshot, scanChan chan<- string) (err error) { +func (f *sendReceiveFolder) deleteItemOnDisk(item protocol.FileInfo, scanChan chan<- string) (err error) { defer func() { if err != nil { err = fmt.Errorf("%s: %w", contextRemovingOldItem, err) @@ -1920,7 +1907,7 @@ func (f *sendReceiveFolder) deleteItemOnDisk(item protocol.FileInfo, snap *db.Sn case item.IsDirectory(): // Directories aren't archived and need special treatment due // to potential children. - return f.deleteDirOnDisk(item.Name, snap, scanChan) + return f.deleteDirOnDisk(item.Name, scanChan) case !item.IsSymlink() && f.versioner != nil: // If we should use versioning, let the versioner archive the @@ -1936,12 +1923,12 @@ func (f *sendReceiveFolder) deleteItemOnDisk(item protocol.FileInfo, snap *db.Sn // deleteDirOnDisk attempts to delete a directory. It checks for files/dirs inside // the directory and removes them if possible or returns an error if it fails -func (f *sendReceiveFolder) deleteDirOnDisk(dir string, snap *db.Snapshot, scanChan chan<- string) error { +func (f *sendReceiveFolder) deleteDirOnDisk(dir string, scanChan chan<- string) error { if err := osutil.TraversesSymlink(f.mtimefs, filepath.Dir(dir)); err != nil { return err } - if err := f.deleteDirOnDiskHandleChildren(dir, snap, scanChan); err != nil { + if err := f.deleteDirOnDiskHandleChildren(dir, scanChan); err != nil { return err } @@ -1961,7 +1948,7 @@ func (f *sendReceiveFolder) deleteDirOnDisk(dir string, snap *db.Snapshot, scanC return err } -func (f *sendReceiveFolder) deleteDirOnDiskHandleChildren(dir string, snap *db.Snapshot, scanChan chan<- string) error { +func (f *sendReceiveFolder) deleteDirOnDiskHandleChildren(dir string, scanChan chan<- string) error { var dirsToDelete []string var hasIgnored, hasKnown, hasToBeScanned, hasReceiveOnlyChanged bool var delErr error @@ -1988,7 +1975,10 @@ func (f *sendReceiveFolder) deleteDirOnDiskHandleChildren(dir string, snap *db.S hasIgnored = true return nil } - cf, ok := snap.Get(protocol.LocalDeviceID, path) + cf, ok, err := f.model.sdb.GetDeviceFile(f.folderID, protocol.LocalDeviceID, path) + if err != nil { + return err + } switch { case !ok || cf.IsDeleted(): // Something appeared in the dir that we either are not @@ -2006,7 +1996,7 @@ func (f *sendReceiveFolder) deleteDirOnDiskHandleChildren(dir string, snap *db.S // Lets just assume the file has changed. scanChan <- path hasToBeScanned = true - return nil + return nil //nolint:nilerr } if !cf.IsEquivalentOptional(diskFile, protocol.FileInfoComparison{ ModTimeWindow: f.modTimeWindow, @@ -2065,7 +2055,7 @@ func (f *sendReceiveFolder) deleteDirOnDiskHandleChildren(dir string, snap *db.S // not changed. func (f *sendReceiveFolder) scanIfItemChanged(name string, stat fs.FileInfo, item protocol.FileInfo, hasItem bool, fromDelete bool, scanChan chan<- string) (err error) { defer func() { - if err == errModified { + if errors.Is(err, errModified) { scanChan <- name } }() diff --git a/lib/model/folder_sendrecv_test.go b/lib/model/folder_sendrecv_test.go index 69b35c018..5eff7a19c 100644 --- a/lib/model/folder_sendrecv_test.go +++ b/lib/model/folder_sendrecv_test.go @@ -14,18 +14,19 @@ import ( "io" "os" "path/filepath" + "runtime/pprof" "strconv" "strings" "testing" "time" + "github.com/syncthing/syncthing/internal/itererr" "github.com/syncthing/syncthing/lib/build" "github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/events" "github.com/syncthing/syncthing/lib/fs" "github.com/syncthing/syncthing/lib/ignore" "github.com/syncthing/syncthing/lib/protocol" - "github.com/syncthing/syncthing/lib/rand" "github.com/syncthing/syncthing/lib/scanner" "github.com/syncthing/syncthing/lib/sync" ) @@ -150,7 +151,7 @@ func TestHandleFile(t *testing.T) { copyChan := make(chan copyBlocksState, 1) - f.handleFile(requiredFile, fsetSnapshot(t, f.fset), copyChan) + f.handleFile(requiredFile, copyChan) // Receive the results toCopy := <-copyChan @@ -190,13 +191,13 @@ func TestHandleFileWithTemp(t *testing.T) { _, f, wcfgCancel := setupSendReceiveFolder(t, existingFile) defer wcfgCancel() - if _, err := prepareTmpFile(f.Filesystem(nil)); err != nil { + if _, err := prepareTmpFile(f.Filesystem()); err != nil { t.Fatal(err) } copyChan := make(chan copyBlocksState, 1) - f.handleFile(requiredFile, fsetSnapshot(t, f.fset), copyChan) + f.handleFile(requiredFile, copyChan) // Receive the results toCopy := <-copyChan @@ -240,7 +241,7 @@ func TestCopierFinder(t *testing.T) { _, f, wcfgCancel := setupSendReceiveFolder(t, existingFile) defer wcfgCancel() - if _, err := prepareTmpFile(f.Filesystem(nil)); err != nil { + if _, err := prepareTmpFile(f.Filesystem()); err != nil { t.Fatal(err) } @@ -252,7 +253,7 @@ func TestCopierFinder(t *testing.T) { go f.copierRoutine(copyChan, pullChan, finisherChan) defer close(copyChan) - f.handleFile(requiredFile, fsetSnapshot(t, f.fset), copyChan) + f.handleFile(requiredFile, copyChan) timeout := time.After(10 * time.Second) pulls := make([]pullBlockState, 4) @@ -273,8 +274,9 @@ func TestCopierFinder(t *testing.T) { defer cleanupSharedPullerState(finish) select { - case <-pullChan: - t.Fatal("Pull channel has data to be read") + case v := <-pullChan: + t.Logf("%+v\n", v) + t.Fatal("Pull channel had data to be read") case <-finisherChan: t.Fatal("Finisher channel has data to be read") default: @@ -300,7 +302,7 @@ func TestCopierFinder(t *testing.T) { } // Verify that the fetched blocks have actually been written to the temp file - blks, err := scanner.HashFile(context.TODO(), f.ID, f.Filesystem(nil), tempFile, protocol.MinBlockSize, nil, false) + blks, err := scanner.HashFile(context.TODO(), f.ID, f.Filesystem(), tempFile, protocol.MinBlockSize, nil) if err != nil { t.Log(err) } @@ -312,134 +314,8 @@ func TestCopierFinder(t *testing.T) { } } -func TestWeakHash(t *testing.T) { - // Setup the model/pull environment - _, fo, wcfgCancel := setupSendReceiveFolder(t) - defer wcfgCancel() - ffs := fo.Filesystem(nil) - - tempFile := fs.TempName("weakhash") - var shift int64 = 10 - var size int64 = 1 << 20 - expectBlocks := int(size / protocol.MinBlockSize) - expectPulls := int(shift / protocol.MinBlockSize) - if shift > 0 { - expectPulls++ - } - - f, err := ffs.Create("weakhash") - must(t, err) - defer f.Close() - _, err = io.CopyN(f, rand.Reader, size) - if err != nil { - t.Error(err) - } - info, err := f.Stat() - if err != nil { - t.Error(err) - } - - // Create two files, second file has `shifted` bytes random prefix, yet - // both are of the same length, for example: - // File 1: abcdefgh - // File 2: xyabcdef - f.Seek(0, io.SeekStart) - existing, err := scanner.Blocks(context.TODO(), f, protocol.MinBlockSize, size, nil, true) - if err != nil { - t.Error(err) - } - - f.Seek(0, io.SeekStart) - remainder := io.LimitReader(f, size-shift) - prefix := io.LimitReader(rand.Reader, shift) - nf := io.MultiReader(prefix, remainder) - desired, err := scanner.Blocks(context.TODO(), nf, protocol.MinBlockSize, size, nil, true) - if err != nil { - t.Error(err) - } - - existingFile := protocol.FileInfo{ - Name: "weakhash", - Blocks: existing, - Size: size, - ModifiedS: info.ModTime().Unix(), - ModifiedNs: int32(info.ModTime().Nanosecond()), - } - desiredFile := protocol.FileInfo{ - Name: "weakhash", - Size: size, - Blocks: desired, - ModifiedS: info.ModTime().Unix() + 1, - } - - fo.updateLocalsFromScanning([]protocol.FileInfo{existingFile}) - - copyChan := make(chan copyBlocksState) - pullChan := make(chan pullBlockState, expectBlocks) - finisherChan := make(chan *sharedPullerState, 1) - - // Run a single fetcher routine - go fo.copierRoutine(copyChan, pullChan, finisherChan) - defer close(copyChan) - - // Test 1 - no weak hashing, file gets fully repulled (`expectBlocks` pulls). - fo.WeakHashThresholdPct = 101 - fo.handleFile(desiredFile, fsetSnapshot(t, fo.fset), copyChan) - - var pulls []pullBlockState - timeout := time.After(10 * time.Second) - for len(pulls) < expectBlocks { - select { - case pull := <-pullChan: - pulls = append(pulls, pull) - case <-timeout: - t.Fatalf("timed out, got %d pulls expected %d", len(pulls), expectPulls) - } - } - finish := <-finisherChan - - select { - case <-pullChan: - t.Fatal("Pull channel has data to be read") - case <-finisherChan: - t.Fatal("Finisher channel has data to be read") - default: - } - - cleanupSharedPullerState(finish) - if err := ffs.Remove(tempFile); err != nil { - t.Fatal(err) - } - - // Test 2 - using weak hash, expectPulls blocks pulled. - fo.WeakHashThresholdPct = -1 - fo.handleFile(desiredFile, fsetSnapshot(t, fo.fset), copyChan) - - pulls = pulls[:0] - for len(pulls) < expectPulls { - select { - case pull := <-pullChan: - pulls = append(pulls, pull) - case <-time.After(10 * time.Second): - t.Fatalf("timed out, got %d pulls expected %d", len(pulls), expectPulls) - } - } - - finish = <-finisherChan - cleanupSharedPullerState(finish) - - expectShifted := expectBlocks - expectPulls - if finish.copyOriginShifted != expectShifted { - t.Errorf("did not copy %d shifted", expectShifted) - } -} - // Test that updating a file removes its old blocks from the blockmap func TestCopierCleanup(t *testing.T) { - iterFn := func(folder, file string, index int32) bool { - return true - } - // Create a file file := setupFile("test", []int{0}) file.Size = 1 @@ -451,11 +327,11 @@ func TestCopierCleanup(t *testing.T) { // Update index (removing old blocks) f.updateLocalsFromScanning([]protocol.FileInfo{file}) - if m.finder.Iterate(folders, blocks[0].Hash, iterFn) { + if vals, err := itererr.Collect(m.sdb.AllLocalBlocksWithHash(f.ID, blocks[0].Hash)); err != nil || len(vals) > 0 { t.Error("Unexpected block found") } - if !m.finder.Iterate(folders, blocks[1].Hash, iterFn) { + if vals, err := itererr.Collect(m.sdb.AllLocalBlocksWithHash(f.ID, blocks[1].Hash)); err != nil || len(vals) == 0 { t.Error("Expected block not found") } @@ -464,11 +340,11 @@ func TestCopierCleanup(t *testing.T) { // Update index (removing old blocks) f.updateLocalsFromScanning([]protocol.FileInfo{file}) - if !m.finder.Iterate(folders, blocks[0].Hash, iterFn) { + if vals, err := itererr.Collect(m.sdb.AllLocalBlocksWithHash(f.ID, blocks[0].Hash)); err != nil || len(vals) == 0 { t.Error("Unexpected block found") } - if m.finder.Iterate(folders, blocks[1].Hash, iterFn) { + if vals, err := itererr.Collect(m.sdb.AllLocalBlocksWithHash(f.ID, blocks[1].Hash)); err != nil || len(vals) > 0 { t.Error("Expected block not found") } } @@ -494,10 +370,9 @@ func TestDeregisterOnFailInCopy(t *testing.T) { finisherBufferChan := make(chan *sharedPullerState, 1) finisherChan := make(chan *sharedPullerState) dbUpdateChan := make(chan dbUpdateJob, 1) - snap := fsetSnapshot(t, f.fset) copyChan, copyWg := startCopier(f, pullChan, finisherBufferChan) - go f.finisherRoutine(snap, finisherChan, dbUpdateChan, make(chan string)) + go f.finisherRoutine(finisherChan, dbUpdateChan, make(chan string)) defer func() { close(copyChan) @@ -507,7 +382,7 @@ func TestDeregisterOnFailInCopy(t *testing.T) { close(finisherChan) }() - f.handleFile(file, snap, copyChan) + f.handleFile(file, copyChan) // Receive a block at puller, to indicate that at least a single copier // loop has been performed. @@ -594,16 +469,15 @@ func TestDeregisterOnFailInPull(t *testing.T) { finisherBufferChan := make(chan *sharedPullerState) finisherChan := make(chan *sharedPullerState) dbUpdateChan := make(chan dbUpdateJob, 1) - snap := fsetSnapshot(t, f.fset) copyChan, copyWg := startCopier(f, pullChan, finisherBufferChan) pullWg := sync.NewWaitGroup() pullWg.Add(1) go func() { - f.pullerRoutine(snap, pullChan, finisherBufferChan) + f.pullerRoutine(pullChan, finisherBufferChan) pullWg.Done() }() - go f.finisherRoutine(snap, finisherChan, dbUpdateChan, make(chan string)) + go f.finisherRoutine(finisherChan, dbUpdateChan, make(chan string)) defer func() { // Unblock copier and puller go func() { @@ -618,7 +492,7 @@ func TestDeregisterOnFailInPull(t *testing.T) { close(finisherChan) }() - f.handleFile(file, snap, copyChan) + f.handleFile(file, copyChan) // Receive at finisher, we should error out as puller has nowhere to pull // from. @@ -681,7 +555,7 @@ func TestDeregisterOnFailInPull(t *testing.T) { func TestIssue3164(t *testing.T) { _, f, wcfgCancel := setupSendReceiveFolder(t) defer wcfgCancel() - ffs := f.Filesystem(nil) + ffs := f.Filesystem() ignDir := filepath.Join("issue3164", "oktodelete") subDir := filepath.Join(ignDir, "foobar") @@ -700,7 +574,7 @@ func TestIssue3164(t *testing.T) { dbUpdateChan := make(chan dbUpdateJob, 1) - f.deleteDir(file, fsetSnapshot(t, f.fset), dbUpdateChan, make(chan string)) + f.deleteDir(file, dbUpdateChan, make(chan string)) if _, err := ffs.Stat("issue3164"); !fs.IsNotExist(err) { t.Fatal(err) @@ -709,8 +583,8 @@ func TestIssue3164(t *testing.T) { func TestDiff(t *testing.T) { for i, test := range diffTestData { - a, _ := scanner.Blocks(context.TODO(), bytes.NewBufferString(test.a), test.s, -1, nil, false) - b, _ := scanner.Blocks(context.TODO(), bytes.NewBufferString(test.b), test.s, -1, nil, false) + a, _ := scanner.Blocks(context.TODO(), bytes.NewBufferString(test.a), test.s, -1, nil) + b, _ := scanner.Blocks(context.TODO(), bytes.NewBufferString(test.b), test.s, -1, nil) _, d := blockDiff(a, b) if len(d) != len(test.d) { t.Fatalf("Incorrect length for diff %d; %d != %d", i, len(d), len(test.d)) @@ -730,8 +604,8 @@ func TestDiff(t *testing.T) { func BenchmarkDiff(b *testing.B) { testCases := make([]struct{ a, b []protocol.BlockInfo }, 0, len(diffTestData)) for _, test := range diffTestData { - a, _ := scanner.Blocks(context.TODO(), bytes.NewBufferString(test.a), test.s, -1, nil, false) - b, _ := scanner.Blocks(context.TODO(), bytes.NewBufferString(test.b), test.s, -1, nil, false) + a, _ := scanner.Blocks(context.TODO(), bytes.NewBufferString(test.a), test.s, -1, nil) + b, _ := scanner.Blocks(context.TODO(), bytes.NewBufferString(test.b), test.s, -1, nil) testCases = append(testCases, struct{ a, b []protocol.BlockInfo }{a, b}) } b.ReportAllocs() @@ -771,7 +645,7 @@ func TestDiffEmpty(t *testing.T) { func TestDeleteIgnorePerms(t *testing.T) { _, f, wcfgCancel := setupSendReceiveFolder(t) defer wcfgCancel() - ffs := f.Filesystem(nil) + ffs := f.Filesystem() f.IgnorePerms = true name := "deleteIgnorePerms" @@ -807,6 +681,17 @@ func TestCopyOwner(t *testing.T) { expGroup = 5678 ) + // This test hung on a regression, taking a long time to fail - speed that up. + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + go func() { + <-ctx.Done() + if errors.Is(ctx.Err(), context.DeadlineExceeded) { + pprof.Lookup("goroutine").WriteTo(os.Stdout, 2) + panic("timed out before test finished") + } + }() + // Set up a folder with the CopyParentOwner bit and backed by a fake // filesystem. @@ -815,9 +700,6 @@ func TestCopyOwner(t *testing.T) { f.folder.FolderConfiguration = newFolderConfiguration(m.cfg, f.ID, f.Label, config.FilesystemTypeFake, "/TestCopyOwner") f.folder.FolderConfiguration.CopyOwnershipFromParent = true - f.fset = newFileSet(t, f.ID, m.db) - f.mtimefs = f.Filesystem(f.fset) - // Create a parent dir with a certain owner/group. f.mtimefs.Mkdir("foo", 0o755) @@ -835,7 +717,7 @@ func TestCopyOwner(t *testing.T) { dbUpdateChan := make(chan dbUpdateJob, 1) scanChan := make(chan string) defer close(dbUpdateChan) - f.handleDir(dir, fsetSnapshot(t, f.fset), dbUpdateChan, scanChan) + f.handleDir(dir, dbUpdateChan, scanChan) select { case <-dbUpdateChan: // empty the channel for later case toScan := <-scanChan: @@ -865,17 +747,16 @@ func TestCopyOwner(t *testing.T) { // but it's the way data is passed around. When the database update // comes the finisher is done. - snap := fsetSnapshot(t, f.fset) finisherChan := make(chan *sharedPullerState) copierChan, copyWg := startCopier(f, nil, finisherChan) - go f.finisherRoutine(snap, finisherChan, dbUpdateChan, nil) + go f.finisherRoutine(finisherChan, dbUpdateChan, nil) defer func() { close(copierChan) copyWg.Wait() close(finisherChan) }() - f.handleFile(file, snap, copierChan) + f.handleFile(file, copierChan) <-dbUpdateChan info, err = f.mtimefs.Lstat("foo/bar/baz") @@ -894,7 +775,7 @@ func TestCopyOwner(t *testing.T) { SymlinkTarget: []byte("over the rainbow"), } - f.handleSymlink(symlink, snap, dbUpdateChan, scanChan) + f.handleSymlink(symlink, dbUpdateChan, scanChan) select { case <-dbUpdateChan: case toScan := <-scanChan: @@ -915,7 +796,7 @@ func TestCopyOwner(t *testing.T) { func TestSRConflictReplaceFileByDir(t *testing.T) { _, f, wcfgCancel := setupSendReceiveFolder(t) defer wcfgCancel() - ffs := f.Filesystem(nil) + ffs := f.Filesystem() name := "foo" @@ -933,7 +814,7 @@ func TestSRConflictReplaceFileByDir(t *testing.T) { dbUpdateChan := make(chan dbUpdateJob, 1) scanChan := make(chan string, 1) - f.handleDir(file, fsetSnapshot(t, f.fset), dbUpdateChan, scanChan) + f.handleDir(file, dbUpdateChan, scanChan) if confls := existingConflicts(name, ffs); len(confls) != 1 { t.Fatal("Expected one conflict, got", len(confls)) @@ -947,7 +828,7 @@ func TestSRConflictReplaceFileByDir(t *testing.T) { func TestSRConflictReplaceFileByLink(t *testing.T) { _, f, wcfgCancel := setupSendReceiveFolder(t) defer wcfgCancel() - ffs := f.Filesystem(nil) + ffs := f.Filesystem() name := "foo" @@ -966,7 +847,7 @@ func TestSRConflictReplaceFileByLink(t *testing.T) { dbUpdateChan := make(chan dbUpdateJob, 1) scanChan := make(chan string, 1) - f.handleSymlink(file, fsetSnapshot(t, f.fset), dbUpdateChan, scanChan) + f.handleSymlink(file, dbUpdateChan, scanChan) if confls := existingConflicts(name, ffs); len(confls) != 1 { t.Fatal("Expected one conflict, got", len(confls)) @@ -980,7 +861,7 @@ func TestSRConflictReplaceFileByLink(t *testing.T) { func TestDeleteBehindSymlink(t *testing.T) { _, f, wcfgCancel := setupSendReceiveFolder(t) defer wcfgCancel() - ffs := f.Filesystem(nil) + ffs := f.Filesystem() link := "link" linkFile := filepath.Join(link, "file") @@ -996,7 +877,7 @@ func TestDeleteBehindSymlink(t *testing.T) { fi.Version = fi.Version.Update(device1.Short()) scanChan := make(chan string, 1) dbUpdateChan := make(chan dbUpdateJob, 1) - f.deleteFile(fi, fsetSnapshot(t, f.fset), dbUpdateChan, scanChan) + f.deleteFile(fi, dbUpdateChan, scanChan) select { case f := <-scanChan: t.Fatalf("Received %v on scanChan", f) @@ -1026,7 +907,7 @@ func TestPullCtxCancel(t *testing.T) { var cancel context.CancelFunc f.ctx, cancel = context.WithCancel(context.Background()) - go f.pullerRoutine(fsetSnapshot(t, f.fset), pullChan, finisherChan) + go f.pullerRoutine(pullChan, finisherChan) defer close(pullChan) emptyState := func() pullBlockState { @@ -1061,7 +942,7 @@ func TestPullCtxCancel(t *testing.T) { func TestPullDeleteUnscannedDir(t *testing.T) { _, f, wcfgCancel := setupSendReceiveFolder(t) defer wcfgCancel() - ffs := f.Filesystem(nil) + ffs := f.Filesystem() dir := "foobar" must(t, ffs.MkdirAll(dir, 0o777)) @@ -1072,7 +953,7 @@ func TestPullDeleteUnscannedDir(t *testing.T) { scanChan := make(chan string, 1) dbUpdateChan := make(chan dbUpdateJob, 1) - f.deleteDir(fi, fsetSnapshot(t, f.fset), dbUpdateChan, scanChan) + f.deleteDir(fi, dbUpdateChan, scanChan) if _, err := ffs.Stat(dir); fs.IsNotExist(err) { t.Error("directory has been deleted") @@ -1090,7 +971,7 @@ func TestPullDeleteUnscannedDir(t *testing.T) { func TestPullCaseOnlyPerformFinish(t *testing.T) { m, f, wcfgCancel := setupSendReceiveFolder(t) defer wcfgCancel() - ffs := f.Filesystem(nil) + ffs := f.Filesystem() name := "foo" contents := []byte("contents") @@ -1099,16 +980,17 @@ func TestPullCaseOnlyPerformFinish(t *testing.T) { var cur protocol.FileInfo hasCur := false - snap := dbSnapshot(t, m, f.ID) - defer snap.Release() - snap.WithHave(protocol.LocalDeviceID, func(i protocol.FileInfo) bool { + it, errFn := m.LocalFiles(f.ID, protocol.LocalDeviceID) + for i := range it { if hasCur { t.Fatal("got more than one file") } cur = i hasCur = true - return true - }) + } + if err := errFn(); err != nil { + t.Fatal(err) + } if !hasCur { t.Fatal("file is missing") } @@ -1122,7 +1004,7 @@ func TestPullCaseOnlyPerformFinish(t *testing.T) { scanChan := make(chan string, 1) dbUpdateChan := make(chan dbUpdateJob, 1) - err := f.performFinish(remote, cur, hasCur, temp, snap, dbUpdateChan, scanChan) + err := f.performFinish(remote, cur, hasCur, temp, dbUpdateChan, scanChan) select { case <-dbUpdateChan: // boring case sensitive filesystem @@ -1132,7 +1014,7 @@ func TestPullCaseOnlyPerformFinish(t *testing.T) { default: } - var caseErr *fs.ErrCaseConflict + var caseErr *fs.CaseConflictError if !errors.As(err, &caseErr) { t.Error("Expected case conflict error, got", err) } @@ -1152,7 +1034,7 @@ func TestPullCaseOnlySymlink(t *testing.T) { func testPullCaseOnlyDirOrSymlink(t *testing.T, dir bool) { m, f, wcfgCancel := setupSendReceiveFolder(t) defer wcfgCancel() - ffs := f.Filesystem(nil) + ffs := f.Filesystem() name := "foo" if dir { @@ -1164,16 +1046,17 @@ func testPullCaseOnlyDirOrSymlink(t *testing.T, dir bool) { must(t, f.scanSubdirs(nil)) var cur protocol.FileInfo hasCur := false - snap := dbSnapshot(t, m, f.ID) - defer snap.Release() - snap.WithHave(protocol.LocalDeviceID, func(i protocol.FileInfo) bool { + it, errFn := m.LocalFiles(f.ID, protocol.LocalDeviceID) + for i := range it { if hasCur { t.Fatal("got more than one file") } cur = i hasCur = true - return true - }) + } + if err := errFn(); err != nil { + t.Fatal(err) + } if !hasCur { t.Fatal("file is missing") } @@ -1186,9 +1069,9 @@ func testPullCaseOnlyDirOrSymlink(t *testing.T, dir bool) { remote.Name = strings.ToUpper(cur.Name) if dir { - f.handleDir(remote, snap, dbUpdateChan, scanChan) + f.handleDir(remote, dbUpdateChan, scanChan) } else { - f.handleSymlink(remote, snap, dbUpdateChan, scanChan) + f.handleSymlink(remote, dbUpdateChan, scanChan) } select { @@ -1223,7 +1106,7 @@ func TestPullTempFileCaseConflict(t *testing.T) { fd.Close() } - f.handleFile(file, fsetSnapshot(t, f.fset), copyChan) + f.handleFile(file, copyChan) cs := <-copyChan if _, err := cs.tempFile(); err != nil { @@ -1265,9 +1148,7 @@ func TestPullCaseOnlyRename(t *testing.T) { dbUpdateChan := make(chan dbUpdateJob, 2) scanChan := make(chan string, 2) - snap := fsetSnapshot(t, f.fset) - defer snap.Release() - if err := f.renameFile(cur, deleted, confl, snap, dbUpdateChan, scanChan); err != nil { + if err := f.renameFile(cur, deleted, confl, dbUpdateChan, scanChan); err != nil { t.Error(err) } } @@ -1342,9 +1223,7 @@ func TestPullDeleteCaseConflict(t *testing.T) { t.Error("Missing db update for file") } - snap := fsetSnapshot(t, f.fset) - defer snap.Release() - f.deleteDir(fi, snap, dbUpdateChan, scanChan) + f.deleteDir(fi, dbUpdateChan, scanChan) select { case <-dbUpdateChan: default: @@ -1372,7 +1251,7 @@ func TestPullDeleteIgnoreChildDir(t *testing.T) { scanChan := make(chan string, 2) - err := f.deleteDirOnDisk(parent, fsetSnapshot(t, f.fset), scanChan) + err := f.deleteDirOnDisk(parent, scanChan) if err == nil { t.Error("no error") } diff --git a/lib/model/folder_summary.go b/lib/model/folder_summary.go index b1d1e77d8..c240f0f90 100644 --- a/lib/model/folder_summary.go +++ b/lib/model/folder_summary.go @@ -11,14 +11,15 @@ package model import ( "context" + "errors" "fmt" "strings" "time" "github.com/thejerf/suture/v4" + "github.com/syncthing/syncthing/internal/db" "github.com/syncthing/syncthing/lib/config" - "github.com/syncthing/syncthing/lib/db" "github.com/syncthing/syncthing/lib/events" "github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/svcutil" @@ -125,28 +126,24 @@ func (c *folderSummaryService) Summary(folder string) (*FolderSummary, error) { var local, global, need, ro db.Counts var ourSeq int64 var remoteSeq map[protocol.DeviceID]int64 - errors, err := c.model.FolderErrors(folder) + errs, err := c.model.FolderErrors(folder) if err == nil { - var snap *db.Snapshot - if snap, err = c.model.DBSnapshot(folder); err == nil { - global = snap.GlobalSize() - local = snap.LocalSize() - need = snap.NeedSize(protocol.LocalDeviceID) - ro = snap.ReceiveOnlyChangedSize() - ourSeq = snap.Sequence(protocol.LocalDeviceID) - remoteSeq = snap.RemoteSequences() - snap.Release() - } + global, _ = c.model.GlobalSize(folder) + local, _ = c.model.LocalSize(folder, protocol.LocalDeviceID) + need, _ = c.model.NeedSize(folder, protocol.LocalDeviceID) + ro, _ = c.model.ReceiveOnlySize(folder) + ourSeq, _ = c.model.Sequence(folder, protocol.LocalDeviceID) + remoteSeq, _ = c.model.RemoteSequences(folder) } // For API backwards compatibility (SyncTrayzor needs it) an empty folder // summary is returned for not running folders, an error might actually be // more appropriate - if err != nil && err != ErrFolderPaused && err != ErrFolderNotRunning { + if err != nil && !errors.Is(err, ErrFolderPaused) && !errors.Is(err, ErrFolderNotRunning) { return nil, err } - res.Errors = len(errors) - res.PullErrors = len(errors) // deprecated + res.Errors = len(errs) + res.PullErrors = len(errs) // deprecated res.Invalid = "" // Deprecated, retains external API for now diff --git a/lib/model/indexhandler.go b/lib/model/indexhandler.go index 23fe49ac6..6ba8e34c7 100644 --- a/lib/model/indexhandler.go +++ b/lib/model/indexhandler.go @@ -8,12 +8,14 @@ package model import ( "context" + "errors" "fmt" "sync" "time" + "github.com/syncthing/syncthing/internal/db" + "github.com/syncthing/syncthing/internal/itererr" "github.com/syncthing/syncthing/lib/config" - "github.com/syncthing/syncthing/lib/db" "github.com/syncthing/syncthing/lib/events" "github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/svcutil" @@ -45,20 +47,27 @@ type indexHandler struct { cond *sync.Cond paused bool - fset *db.FileSet + sdb db.DB runner service } -func newIndexHandler(conn protocol.Connection, downloads *deviceDownloadState, folder config.FolderConfiguration, fset *db.FileSet, runner service, startInfo *clusterConfigDeviceInfo, evLogger events.Logger) *indexHandler { - myIndexID := fset.IndexID(protocol.LocalDeviceID) - mySequence := fset.Sequence(protocol.LocalDeviceID) +func newIndexHandler(conn protocol.Connection, downloads *deviceDownloadState, folder config.FolderConfiguration, sdb db.DB, runner service, startInfo *clusterConfigDeviceInfo, evLogger events.Logger) (*indexHandler, error) { + myIndexID, err := sdb.GetIndexID(folder.ID, protocol.LocalDeviceID) + if err != nil { + return nil, err + } + mySequence, err := sdb.GetDeviceSequence(folder.ID, protocol.LocalDeviceID) + if err != nil { + return nil, err + } var startSequence int64 // This is the other side's description of what it knows // about us. Lets check to see if we can start sending index // updates directly or need to send the index from start... - if startInfo.local.IndexID == myIndexID { + switch startInfo.local.IndexID { + case myIndexID: // They say they've seen our index ID before, so we can // send a delta update only. @@ -75,15 +84,17 @@ func newIndexHandler(conn protocol.Connection, downloads *deviceDownloadState, f l.Debugf("Device %v folder %s is delta index compatible (mlv=%d)", conn.DeviceID().Short(), folder.Description(), startInfo.local.MaxSequence) startSequence = startInfo.local.MaxSequence } - } else if startInfo.local.IndexID != 0 { + + case 0: + l.Debugf("Device %v folder %s has no index ID for us", conn.DeviceID().Short(), folder.Description()) + + default: // They say they've seen an index ID from us, but it's // not the right one. Either they are confused or we // must have reset our database since last talking to // them. We'll start with a full index transfer. l.Infof("Device %v folder %s has mismatching index ID for us (%v != %v)", conn.DeviceID().Short(), folder.Description(), startInfo.local.IndexID, myIndexID) startSequence = 0 - } else { - l.Debugf("Device %v folder %s has no index ID for us", conn.DeviceID().Short(), folder.Description()) } // This is the other side's description of themselves. We @@ -91,14 +102,16 @@ func newIndexHandler(conn protocol.Connection, downloads *deviceDownloadState, f // otherwise we drop our old index data and expect to get a // completely new set. - theirIndexID := fset.IndexID(conn.DeviceID()) + theirIndexID, _ := sdb.GetIndexID(folder.ID, conn.DeviceID()) if startInfo.remote.IndexID == 0 { // They're not announcing an index ID. This means they // do not support delta indexes and we should clear any // information we have from them before accepting their // index, which will presumably be a full index. l.Debugf("Device %v folder %s does not announce an index ID", conn.DeviceID().Short(), folder.Description()) - fset.Drop(conn.DeviceID()) + if err := sdb.DropAllFiles(folder.ID, conn.DeviceID()); err != nil { + return nil, err + } } else if startInfo.remote.IndexID != theirIndexID { // The index ID we have on file is not what they're // announcing. They must have reset their database and @@ -106,8 +119,12 @@ func newIndexHandler(conn protocol.Connection, downloads *deviceDownloadState, f // information we have and remember this new index ID // instead. l.Infof("Device %v folder %s has a new index ID (%v)", conn.DeviceID().Short(), folder.Description(), startInfo.remote.IndexID) - fset.Drop(conn.DeviceID()) - fset.SetIndexID(conn.DeviceID(), startInfo.remote.IndexID) + if err := sdb.DropAllFiles(folder.ID, conn.DeviceID()); err != nil { + return nil, err + } + if err := sdb.SetIndexID(folder.ID, conn.DeviceID(), startInfo.remote.IndexID); err != nil { + return nil, err + } } return &indexHandler{ @@ -119,27 +136,27 @@ func newIndexHandler(conn protocol.Connection, downloads *deviceDownloadState, f sentPrevSequence: startSequence, evLogger: evLogger, - fset: fset, + sdb: sdb, runner: runner, cond: sync.NewCond(new(sync.Mutex)), - } + }, nil } -// waitForFileset waits for the handler to resume and fetches the current fileset. -func (s *indexHandler) waitForFileset(ctx context.Context) (*db.FileSet, error) { +// waitWhilePaused waits for the handler to resume. +func (s *indexHandler) waitWhilePaused(ctx context.Context) error { s.cond.L.Lock() defer s.cond.L.Unlock() for s.paused { select { case <-ctx.Done(): - return nil, ctx.Err() + return ctx.Err() default: s.cond.Wait() } } - return s.fset, nil + return nil } func (s *indexHandler) Serve(ctx context.Context) (err error) { @@ -162,11 +179,10 @@ func (s *indexHandler) Serve(ctx context.Context) (err error) { }() // We need to send one index, regardless of whether there is something to send or not - fset, err := s.waitForFileset(ctx) - if err != nil { + if err := s.waitWhilePaused(ctx); err != nil { return err } - err = s.sendIndexTo(ctx, fset) + err = s.sendIndexTo(ctx) // Subscribe to LocalIndexUpdated (we have new information to send) and // DeviceDisconnected (it might be us who disconnected, so we should @@ -179,8 +195,7 @@ func (s *indexHandler) Serve(ctx context.Context) (err error) { defer ticker.Stop() for err == nil { - fset, err = s.waitForFileset(ctx) - if err != nil { + if err := s.waitWhilePaused(ctx); err != nil { return err } @@ -188,7 +203,12 @@ func (s *indexHandler) Serve(ctx context.Context) (err error) { // currently in the database, wait for the local index to update. The // local index may update for other folders than the one we are // sending for. - if fset.Sequence(protocol.LocalDeviceID) <= s.localPrevSequence { + var seq int64 + seq, err = s.sdb.GetDeviceSequence(s.folder, protocol.LocalDeviceID) + if err != nil { + return err + } + if seq <= s.localPrevSequence { select { case <-ctx.Done(): return ctx.Err() @@ -198,7 +218,7 @@ func (s *indexHandler) Serve(ctx context.Context) (err error) { continue } - err = s.sendIndexTo(ctx, fset) + err = s.sendIndexTo(ctx) // Wait a short amount of time before entering the next loop. If there // are continuous changes happening to the local index, this gives us @@ -215,10 +235,9 @@ func (s *indexHandler) Serve(ctx context.Context) (err error) { // resume might be called because the folder was actually resumed, or just // because the folder config changed (and thus the runner and potentially fset). -func (s *indexHandler) resume(fset *db.FileSet, runner service) { +func (s *indexHandler) resume(runner service) { s.cond.L.Lock() s.paused = false - s.fset = fset s.runner = runner s.cond.Broadcast() s.cond.L.Unlock() @@ -230,7 +249,6 @@ func (s *indexHandler) pause() { s.evLogger.Log(events.Failure, "index handler got paused while already paused") } s.paused = true - s.fset = nil s.runner = nil s.cond.Broadcast() s.cond.L.Unlock() @@ -238,9 +256,9 @@ func (s *indexHandler) pause() { // sendIndexTo sends file infos with a sequence number higher than prevSequence and // returns the highest sent sequence number. -func (s *indexHandler) sendIndexTo(ctx context.Context, fset *db.FileSet) error { +func (s *indexHandler) sendIndexTo(ctx context.Context) error { initial := s.localPrevSequence == 0 - batch := db.NewFileInfoBatch(nil) + batch := NewFileInfoBatch(nil) var batchError error batch.SetFlushFunc(func(fs []protocol.FileInfo) error { select { @@ -284,22 +302,18 @@ func (s *indexHandler) sendIndexTo(ctx context.Context, fset *db.FileSet) error return nil }) - var err error var f protocol.FileInfo - snap, err := fset.Snapshot() - if err != nil { - return svcutil.AsFatalErr(err, svcutil.ExitError) - } - defer snap.Release() previousWasDelete := false - snap.WithHaveSequence(s.localPrevSequence+1, func(fi protocol.FileInfo) bool { + + for fi, err := range itererr.Zip(s.sdb.AllLocalFilesBySequence(s.folder, protocol.LocalDeviceID, s.localPrevSequence+1, MaxBatchSizeFiles+1)) { + if err != nil { + return err + } // This is to make sure that renames (which is an add followed by a delete) land in the same batch. // Even if the batch is full, we allow a last delete to slip in, we do this by making sure that // the batch ends with a non-delete, or that the last item in the batch is already a delete if batch.Full() && (!fi.IsDeleted() || previousWasDelete) { - if err = batch.Flush(); err != nil { - return false - } + break } if fi.SequenceNo() < s.localPrevSequence+1 { @@ -307,6 +321,7 @@ func (s *indexHandler) sendIndexTo(ctx context.Context, fset *db.FileSet) error "sequence": fi.SequenceNo(), "start": s.localPrevSequence + 1, }) + return errors.New("database misbehaved") } if f.Sequence > 0 && fi.SequenceNo() <= f.Sequence { @@ -315,27 +330,17 @@ func (s *indexHandler) sendIndexTo(ctx context.Context, fset *db.FileSet) error "start": s.localPrevSequence + 1, "previous": f.Sequence, }) - // Abort this round of index sending - the next one will pick - // up from the last successful one with the repeaired db. - defer func() { - if fixed, dbErr := fset.RepairSequence(); dbErr != nil { - l.Warnln("Failed repairing sequence entries:", dbErr) - panic("Failed repairing sequence entries") - } else { - s.evLogger.Log(events.Failure, "detected and repaired non-increasing sequence") - l.Infof("Repaired %v sequence entries in database", fixed) - } - }() - return false + return errors.New("database misbehaved") } f = fi + s.localPrevSequence = f.Sequence // If this is a folder receiving encrypted files only, we // mustn't ever send locally changed file infos. Those aren't // encrypted and thus would be a protocol error at the remote. if s.folderIsReceiveEncrypted && fi.IsReceiveOnlyChanged() { - return true + continue } f = prepareFileInfoForIndex(f) @@ -343,24 +348,8 @@ func (s *indexHandler) sendIndexTo(ctx context.Context, fset *db.FileSet) error previousWasDelete = f.IsDeleted() batch.Append(f) - return true - }) - if err != nil { - return err } - - if err := batch.Flush(); err != nil { - return err - } - - // Use the sequence of the snapshot we iterated as a starting point for the - // next run. Previously we used the sequence of the last file we sent, - // however it's possible that a higher sequence exists, just doesn't need to - // be sent (e.g. in a receive-only folder, when a local change was - // reverted). No point trying to send nothing again. - s.localPrevSequence = snap.Sequence(protocol.LocalDeviceID) - - return nil + return batch.Flush() } func (s *indexHandler) receive(fs []protocol.FileInfo, update bool, op string, prevSequence, lastSequence int64) error { @@ -368,7 +357,6 @@ func (s *indexHandler) receive(fs []protocol.FileInfo, update bool, op string, p s.cond.L.Lock() paused := s.paused - fset := s.fset runner := s.runner s.cond.L.Unlock() @@ -382,13 +370,19 @@ func (s *indexHandler) receive(fs []protocol.FileInfo, update bool, op string, p s.downloads.Update(s.folder, makeForgetUpdate(fs)) if !update { - fset.Drop(deviceID) + if err := s.sdb.DropAllFiles(s.folder, deviceID); err != nil { + return err + } } l.Debugf("Received %d files for %s from %s, prevSeq=%d, lastSeq=%d", len(fs), s.folder, deviceID.Short(), prevSequence, lastSequence) // Verify that the previous sequence number matches what we expected - if exp := fset.Sequence(deviceID); prevSequence > 0 && prevSequence != exp { + exp, err := s.sdb.GetDeviceSequence(s.folder, deviceID) + if err != nil { + return err + } + if prevSequence > 0 && prevSequence != exp { s.logSequenceAnomaly("index update with unexpected sequence", map[string]any{ "prevSeq": prevSequence, "lastSeq": lastSequence, @@ -444,8 +438,13 @@ func (s *indexHandler) receive(fs []protocol.FileInfo, update bool, op string, p }) } - fset.Update(deviceID, fs) - seq := fset.Sequence(deviceID) + if err := s.sdb.Update(s.folder, deviceID, fs); err != nil { + return err + } + seq, err := s.sdb.GetDeviceSequence(s.folder, deviceID) + if err != nil { + return err + } // Check that the sequence we get back is what we put in... if lastSequence > 0 && len(fs) > 0 && seq != lastSequence { @@ -508,6 +507,7 @@ func (s *indexHandler) String() string { type indexHandlerRegistry struct { evLogger events.Logger conn protocol.Connection + sdb db.DB downloads *deviceDownloadState indexHandlers *serviceMap[string, *indexHandler] startInfos map[string]*clusterConfigDeviceInfo @@ -517,14 +517,14 @@ type indexHandlerRegistry struct { type indexHandlerFolderState struct { cfg config.FolderConfiguration - fset *db.FileSet runner service } -func newIndexHandlerRegistry(conn protocol.Connection, downloads *deviceDownloadState, evLogger events.Logger) *indexHandlerRegistry { +func newIndexHandlerRegistry(conn protocol.Connection, sdb db.DB, downloads *deviceDownloadState, evLogger events.Logger) *indexHandlerRegistry { r := &indexHandlerRegistry{ evLogger: evLogger, conn: conn, + sdb: sdb, downloads: downloads, indexHandlers: newServiceMap[string, *indexHandler](evLogger), startInfos: make(map[string]*clusterConfigDeviceInfo), @@ -544,15 +544,19 @@ func (r *indexHandlerRegistry) Serve(ctx context.Context) error { return r.indexHandlers.Serve(ctx) } -func (r *indexHandlerRegistry) startLocked(folder config.FolderConfiguration, fset *db.FileSet, runner service, startInfo *clusterConfigDeviceInfo) { +func (r *indexHandlerRegistry) startLocked(folder config.FolderConfiguration, runner service, startInfo *clusterConfigDeviceInfo) error { r.indexHandlers.RemoveAndWait(folder.ID, 0) delete(r.startInfos, folder.ID) - is := newIndexHandler(r.conn, r.downloads, folder, fset, runner, startInfo, r.evLogger) + is, err := newIndexHandler(r.conn, r.downloads, folder, r.sdb, runner, startInfo, r.evLogger) + if err != nil { + return err + } r.indexHandlers.Add(folder.ID, is) // This new connection might help us get in sync. runner.SchedulePull() + return nil } // AddIndexInfo starts an index handler for given folder, unless it is paused. @@ -572,7 +576,7 @@ func (r *indexHandlerRegistry) AddIndexInfo(folder string, startInfo *clusterCon r.startInfos[folder] = startInfo return } - r.startLocked(folderState.cfg, folderState.fset, folderState.runner, startInfo) + _ = r.startLocked(folderState.cfg, folderState.runner, startInfo) // XXX error handling... } // Remove stops a running index handler or removes one pending to be started. @@ -612,7 +616,7 @@ func (r *indexHandlerRegistry) RemoveAllExcept(except map[string]remoteFolderSta // RegisterFolderState must be called whenever something about the folder // changes. The exception being if the folder is removed entirely, then call // Remove. The fset and runner arguments may be nil, if given folder is paused. -func (r *indexHandlerRegistry) RegisterFolderState(folder config.FolderConfiguration, fset *db.FileSet, runner service) { +func (r *indexHandlerRegistry) RegisterFolderState(folder config.FolderConfiguration, runner service) { if !folder.SharedWith(r.conn.DeviceID()) { r.Remove(folder.ID) return @@ -622,7 +626,7 @@ func (r *indexHandlerRegistry) RegisterFolderState(folder config.FolderConfigura if folder.Paused { r.folderPausedLocked(folder.ID) } else { - r.folderRunningLocked(folder, fset, runner) + r.folderRunningLocked(folder, runner) } r.mut.Unlock() } @@ -643,10 +647,9 @@ func (r *indexHandlerRegistry) folderPausedLocked(folder string) { // folderRunningLocked resumes an already running index handler or starts it, if it // was added while paused. // It is a noop if the folder isn't known. -func (r *indexHandlerRegistry) folderRunningLocked(folder config.FolderConfiguration, fset *db.FileSet, runner service) { +func (r *indexHandlerRegistry) folderRunningLocked(folder config.FolderConfiguration, runner service) { r.folderStates[folder.ID] = &indexHandlerFolderState{ cfg: folder, - fset: fset, runner: runner, } @@ -656,12 +659,12 @@ func (r *indexHandlerRegistry) folderRunningLocked(folder config.FolderConfigura r.indexHandlers.RemoveAndWait(folder.ID, 0) l.Debugf("Removed index handler for device %v and folder %v in resume", r.conn.DeviceID().Short(), folder.ID) } - r.startLocked(folder, fset, runner, info) + _ = r.startLocked(folder, runner, info) // XXX error handling... delete(r.startInfos, folder.ID) l.Debugf("Started index handler for device %v and folder %v in resume", r.conn.DeviceID().Short(), folder.ID) } else if isOk { l.Debugf("Resuming index handler for device %v and folder %v", r.conn.DeviceID().Short(), folder) - is.resume(fset, runner) + is.resume(runner) } else { l.Debugf("Not resuming index handler for device %v and folder %v as none is paused and there is no start info", r.conn.DeviceID().Short(), folder.ID) } diff --git a/lib/model/indexhandler_test.go b/lib/model/indexhandler_test.go index e8d74e390..57a532128 100644 --- a/lib/model/indexhandler_test.go +++ b/lib/model/indexhandler_test.go @@ -13,7 +13,7 @@ import ( "sync" "testing" - "github.com/syncthing/syncthing/lib/db" + "github.com/syncthing/syncthing/lib/model" "github.com/syncthing/syncthing/lib/model/mocks" "github.com/syncthing/syncthing/lib/protocol" protomock "github.com/syncthing/syncthing/lib/protocol/mocks" @@ -63,7 +63,7 @@ func TestIndexhandlerConcurrency(t *testing.T) { return nil }) - b1 := db.NewFileInfoBatch(func(fs []protocol.FileInfo) error { + b1 := model.NewFileInfoBatch(func(fs []protocol.FileInfo) error { return c1.IndexUpdate(ctx, &protocol.IndexUpdate{Folder: "foo", Files: fs}) }) sentEntries := 0 diff --git a/lib/model/metrics.go b/lib/model/metrics.go index 6627d027b..24fdff5d9 100644 --- a/lib/model/metrics.go +++ b/lib/model/metrics.go @@ -18,7 +18,7 @@ var ( Name: "folder_state", Help: "Current folder state", }, []string{"folder"}) - metricFolderSummary = promauto.NewGaugeVec(prometheus.GaugeOpts{ + metricFolderSummary = promauto.NewGaugeVec(prometheus.GaugeOpts{ //nolint:promlinter Namespace: "syncthing", Subsystem: "model", Name: "folder_summary", @@ -55,7 +55,7 @@ var ( Namespace: "syncthing", Subsystem: "model", Name: "folder_processed_bytes_total", - Help: "Total amount of data processed during folder syncing, per folder ID and data source (network/local_origin/local_other/local_shifted/skipped)", + Help: "Total amount of data processed during folder syncing, per folder ID and data source (network/local_origin/local_other/skipped)", }, []string{"folder", "source"}) metricFolderConflictsTotal = promauto.NewCounterVec(prometheus.CounterOpts{ @@ -67,11 +67,10 @@ var ( ) const ( - metricSourceNetwork = "network" // from the network - metricSourceLocalOrigin = "local_origin" // from the existing version of the local file - metricSourceLocalOther = "local_other" // from a different local file - metricSourceLocalShifted = "local_shifted" // from the existing version of the local file, rolling hash shifted - metricSourceSkipped = "skipped" // block of all zeroes, invented out of thin air + metricSourceNetwork = "network" // from the network + metricSourceLocalOrigin = "local_origin" // from the existing version of the local file + metricSourceLocalOther = "local_other" // from a different local file + metricSourceSkipped = "skipped" // block of all zeroes, invented out of thin air metricScopeGlobal = "global" metricScopeLocal = "local" @@ -95,7 +94,6 @@ func registerFolderMetrics(folderID string) { metricFolderProcessedBytesTotal.WithLabelValues(folderID, metricSourceNetwork) metricFolderProcessedBytesTotal.WithLabelValues(folderID, metricSourceLocalOrigin) metricFolderProcessedBytesTotal.WithLabelValues(folderID, metricSourceLocalOther) - metricFolderProcessedBytesTotal.WithLabelValues(folderID, metricSourceLocalShifted) metricFolderProcessedBytesTotal.WithLabelValues(folderID, metricSourceSkipped) metricFolderConflictsTotal.WithLabelValues(folderID) } diff --git a/lib/model/mocks/model.go b/lib/model/mocks/model.go index 3855f7e99..cdceb850e 100644 --- a/lib/model/mocks/model.go +++ b/lib/model/mocks/model.go @@ -3,12 +3,12 @@ package mocks import ( "context" + "iter" "net" "sync" "time" - "github.com/syncthing/syncthing/lib/db" - "github.com/syncthing/syncthing/lib/fs" + "github.com/syncthing/syncthing/internal/db" "github.com/syncthing/syncthing/lib/model" "github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/stats" @@ -23,6 +23,19 @@ type Model struct { arg1 protocol.Connection arg2 protocol.Hello } + AllGlobalFilesStub func(string) (iter.Seq[db.FileMetadata], func() error) + allGlobalFilesMutex sync.RWMutex + allGlobalFilesArgsForCall []struct { + arg1 string + } + allGlobalFilesReturns struct { + result1 iter.Seq[db.FileMetadata] + result2 func() error + } + allGlobalFilesReturnsOnCall map[int]struct { + result1 iter.Seq[db.FileMetadata] + result2 func() error + } AvailabilityStub func(string, protocol.FileInfo, protocol.BlockInfo) ([]model.Availability, error) availabilityMutex sync.RWMutex availabilityArgsForCall []struct { @@ -144,19 +157,6 @@ type Model struct { result2 []string result3 error } - DBSnapshotStub func(string) (*db.Snapshot, error) - dBSnapshotMutex sync.RWMutex - dBSnapshotArgsForCall []struct { - arg1 string - } - dBSnapshotReturns struct { - result1 *db.Snapshot - result2 error - } - dBSnapshotReturnsOnCall map[int]struct { - result1 *db.Snapshot - result2 error - } DelayScanStub func(string, time.Duration) delayScanMutex sync.RWMutex delayScanArgsForCall []struct { @@ -259,20 +259,6 @@ type Model struct { result1 map[string][]versioner.FileVersion result2 error } - GetMtimeMappingStub func(string, string) (fs.MtimeMapping, error) - getMtimeMappingMutex sync.RWMutex - getMtimeMappingArgsForCall []struct { - arg1 string - arg2 string - } - getMtimeMappingReturns struct { - result1 fs.MtimeMapping - result2 error - } - getMtimeMappingReturnsOnCall map[int]struct { - result1 fs.MtimeMapping - result2 error - } GlobalDirectoryTreeStub func(string, string, int, bool) ([]*model.TreeEntry, error) globalDirectoryTreeMutex sync.RWMutex globalDirectoryTreeArgsForCall []struct { @@ -289,6 +275,19 @@ type Model struct { result1 []*model.TreeEntry result2 error } + GlobalSizeStub func(string) (db.Counts, error) + globalSizeMutex sync.RWMutex + globalSizeArgsForCall []struct { + arg1 string + } + globalSizeReturns struct { + result1 db.Counts + result2 error + } + globalSizeReturnsOnCall map[int]struct { + result1 db.Counts + result2 error + } IndexStub func(protocol.Connection, *protocol.Index) error indexMutex sync.RWMutex indexArgsForCall []struct { @@ -343,6 +342,49 @@ type Model struct { result1 []protocol.FileInfo result2 error } + LocalFilesStub func(string, protocol.DeviceID) (iter.Seq[protocol.FileInfo], func() error) + localFilesMutex sync.RWMutex + localFilesArgsForCall []struct { + arg1 string + arg2 protocol.DeviceID + } + localFilesReturns struct { + result1 iter.Seq[protocol.FileInfo] + result2 func() error + } + localFilesReturnsOnCall map[int]struct { + result1 iter.Seq[protocol.FileInfo] + result2 func() error + } + LocalFilesSequencedStub func(string, protocol.DeviceID, int64) (iter.Seq[protocol.FileInfo], func() error) + localFilesSequencedMutex sync.RWMutex + localFilesSequencedArgsForCall []struct { + arg1 string + arg2 protocol.DeviceID + arg3 int64 + } + localFilesSequencedReturns struct { + result1 iter.Seq[protocol.FileInfo] + result2 func() error + } + localFilesSequencedReturnsOnCall map[int]struct { + result1 iter.Seq[protocol.FileInfo] + result2 func() error + } + LocalSizeStub func(string, protocol.DeviceID) (db.Counts, error) + localSizeMutex sync.RWMutex + localSizeArgsForCall []struct { + arg1 string + arg2 protocol.DeviceID + } + localSizeReturns struct { + result1 db.Counts + result2 error + } + localSizeReturnsOnCall map[int]struct { + result1 db.Counts + result2 error + } NeedFolderFilesStub func(string, int, int) ([]protocol.FileInfo, []protocol.FileInfo, []protocol.FileInfo, error) needFolderFilesMutex sync.RWMutex needFolderFilesArgsForCall []struct { @@ -362,6 +404,20 @@ type Model struct { result3 []protocol.FileInfo result4 error } + NeedSizeStub func(string, protocol.DeviceID) (db.Counts, error) + needSizeMutex sync.RWMutex + needSizeArgsForCall []struct { + arg1 string + arg2 protocol.DeviceID + } + needSizeReturns struct { + result1 db.Counts + result2 error + } + needSizeReturnsOnCall map[int]struct { + result1 db.Counts + result2 error + } OnHelloStub func(protocol.DeviceID, net.Addr, protocol.Hello) error onHelloMutex sync.RWMutex onHelloArgsForCall []struct { @@ -405,6 +461,19 @@ type Model struct { result1 map[string]db.PendingFolder result2 error } + ReceiveOnlySizeStub func(string) (db.Counts, error) + receiveOnlySizeMutex sync.RWMutex + receiveOnlySizeArgsForCall []struct { + arg1 string + } + receiveOnlySizeReturns struct { + result1 db.Counts + result2 error + } + receiveOnlySizeReturnsOnCall map[int]struct { + result1 db.Counts + result2 error + } RemoteNeedFolderFilesStub func(string, protocol.DeviceID, int, int) ([]protocol.FileInfo, error) remoteNeedFolderFilesMutex sync.RWMutex remoteNeedFolderFilesArgsForCall []struct { @@ -421,6 +490,19 @@ type Model struct { result1 []protocol.FileInfo result2 error } + RemoteSequencesStub func(string) (map[protocol.DeviceID]int64, error) + remoteSequencesMutex sync.RWMutex + remoteSequencesArgsForCall []struct { + arg1 string + } + remoteSequencesReturns struct { + result1 map[protocol.DeviceID]int64 + result2 error + } + remoteSequencesReturnsOnCall map[int]struct { + result1 map[protocol.DeviceID]int64 + result2 error + } RequestStub func(protocol.Connection, *protocol.Request) (protocol.RequestResponse, error) requestMutex sync.RWMutex requestArgsForCall []struct { @@ -435,19 +517,18 @@ type Model struct { result1 protocol.RequestResponse result2 error } - RequestGlobalStub func(context.Context, protocol.DeviceID, string, string, int, int64, int, []byte, uint32, bool) ([]byte, error) + RequestGlobalStub func(context.Context, protocol.DeviceID, string, string, int, int64, int, []byte, bool) ([]byte, error) requestGlobalMutex sync.RWMutex requestGlobalArgsForCall []struct { - arg1 context.Context - arg2 protocol.DeviceID - arg3 string - arg4 string - arg5 int - arg6 int64 - arg7 int - arg8 []byte - arg9 uint32 - arg10 bool + arg1 context.Context + arg2 protocol.DeviceID + arg3 string + arg4 string + arg5 int + arg6 int64 + arg7 int + arg8 []byte + arg9 bool } requestGlobalReturns struct { result1 []byte @@ -520,6 +601,20 @@ type Model struct { scanFoldersReturnsOnCall map[int]struct { result1 map[string]error } + SequenceStub func(string, protocol.DeviceID) (int64, error) + sequenceMutex sync.RWMutex + sequenceArgsForCall []struct { + arg1 string + arg2 protocol.DeviceID + } + sequenceReturns struct { + result1 int64 + result2 error + } + sequenceReturnsOnCall map[int]struct { + result1 int64 + result2 error + } ServeStub func(context.Context) error serveMutex sync.RWMutex serveArgsForCall []struct { @@ -613,6 +708,70 @@ func (fake *Model) AddConnectionArgsForCall(i int) (protocol.Connection, protoco return argsForCall.arg1, argsForCall.arg2 } +func (fake *Model) AllGlobalFiles(arg1 string) (iter.Seq[db.FileMetadata], func() error) { + fake.allGlobalFilesMutex.Lock() + ret, specificReturn := fake.allGlobalFilesReturnsOnCall[len(fake.allGlobalFilesArgsForCall)] + fake.allGlobalFilesArgsForCall = append(fake.allGlobalFilesArgsForCall, struct { + arg1 string + }{arg1}) + stub := fake.AllGlobalFilesStub + fakeReturns := fake.allGlobalFilesReturns + fake.recordInvocation("AllGlobalFiles", []interface{}{arg1}) + fake.allGlobalFilesMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *Model) AllGlobalFilesCallCount() int { + fake.allGlobalFilesMutex.RLock() + defer fake.allGlobalFilesMutex.RUnlock() + return len(fake.allGlobalFilesArgsForCall) +} + +func (fake *Model) AllGlobalFilesCalls(stub func(string) (iter.Seq[db.FileMetadata], func() error)) { + fake.allGlobalFilesMutex.Lock() + defer fake.allGlobalFilesMutex.Unlock() + fake.AllGlobalFilesStub = stub +} + +func (fake *Model) AllGlobalFilesArgsForCall(i int) string { + fake.allGlobalFilesMutex.RLock() + defer fake.allGlobalFilesMutex.RUnlock() + argsForCall := fake.allGlobalFilesArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *Model) AllGlobalFilesReturns(result1 iter.Seq[db.FileMetadata], result2 func() error) { + fake.allGlobalFilesMutex.Lock() + defer fake.allGlobalFilesMutex.Unlock() + fake.AllGlobalFilesStub = nil + fake.allGlobalFilesReturns = struct { + result1 iter.Seq[db.FileMetadata] + result2 func() error + }{result1, result2} +} + +func (fake *Model) AllGlobalFilesReturnsOnCall(i int, result1 iter.Seq[db.FileMetadata], result2 func() error) { + fake.allGlobalFilesMutex.Lock() + defer fake.allGlobalFilesMutex.Unlock() + fake.AllGlobalFilesStub = nil + if fake.allGlobalFilesReturnsOnCall == nil { + fake.allGlobalFilesReturnsOnCall = make(map[int]struct { + result1 iter.Seq[db.FileMetadata] + result2 func() error + }) + } + fake.allGlobalFilesReturnsOnCall[i] = struct { + result1 iter.Seq[db.FileMetadata] + result2 func() error + }{result1, result2} +} + func (fake *Model) Availability(arg1 string, arg2 protocol.FileInfo, arg3 protocol.BlockInfo) ([]model.Availability, error) { fake.availabilityMutex.Lock() ret, specificReturn := fake.availabilityReturnsOnCall[len(fake.availabilityArgsForCall)] @@ -1189,70 +1348,6 @@ func (fake *Model) CurrentIgnoresReturnsOnCall(i int, result1 []string, result2 }{result1, result2, result3} } -func (fake *Model) DBSnapshot(arg1 string) (*db.Snapshot, error) { - fake.dBSnapshotMutex.Lock() - ret, specificReturn := fake.dBSnapshotReturnsOnCall[len(fake.dBSnapshotArgsForCall)] - fake.dBSnapshotArgsForCall = append(fake.dBSnapshotArgsForCall, struct { - arg1 string - }{arg1}) - stub := fake.DBSnapshotStub - fakeReturns := fake.dBSnapshotReturns - fake.recordInvocation("DBSnapshot", []interface{}{arg1}) - fake.dBSnapshotMutex.Unlock() - if stub != nil { - return stub(arg1) - } - if specificReturn { - return ret.result1, ret.result2 - } - return fakeReturns.result1, fakeReturns.result2 -} - -func (fake *Model) DBSnapshotCallCount() int { - fake.dBSnapshotMutex.RLock() - defer fake.dBSnapshotMutex.RUnlock() - return len(fake.dBSnapshotArgsForCall) -} - -func (fake *Model) DBSnapshotCalls(stub func(string) (*db.Snapshot, error)) { - fake.dBSnapshotMutex.Lock() - defer fake.dBSnapshotMutex.Unlock() - fake.DBSnapshotStub = stub -} - -func (fake *Model) DBSnapshotArgsForCall(i int) string { - fake.dBSnapshotMutex.RLock() - defer fake.dBSnapshotMutex.RUnlock() - argsForCall := fake.dBSnapshotArgsForCall[i] - return argsForCall.arg1 -} - -func (fake *Model) DBSnapshotReturns(result1 *db.Snapshot, result2 error) { - fake.dBSnapshotMutex.Lock() - defer fake.dBSnapshotMutex.Unlock() - fake.DBSnapshotStub = nil - fake.dBSnapshotReturns = struct { - result1 *db.Snapshot - result2 error - }{result1, result2} -} - -func (fake *Model) DBSnapshotReturnsOnCall(i int, result1 *db.Snapshot, result2 error) { - fake.dBSnapshotMutex.Lock() - defer fake.dBSnapshotMutex.Unlock() - fake.DBSnapshotStub = nil - if fake.dBSnapshotReturnsOnCall == nil { - fake.dBSnapshotReturnsOnCall = make(map[int]struct { - result1 *db.Snapshot - result2 error - }) - } - fake.dBSnapshotReturnsOnCall[i] = struct { - result1 *db.Snapshot - result2 error - }{result1, result2} -} - func (fake *Model) DelayScan(arg1 string, arg2 time.Duration) { fake.delayScanMutex.Lock() fake.delayScanArgsForCall = append(fake.delayScanArgsForCall, struct { @@ -1772,71 +1867,6 @@ func (fake *Model) GetFolderVersionsReturnsOnCall(i int, result1 map[string][]ve }{result1, result2} } -func (fake *Model) GetMtimeMapping(arg1 string, arg2 string) (fs.MtimeMapping, error) { - fake.getMtimeMappingMutex.Lock() - ret, specificReturn := fake.getMtimeMappingReturnsOnCall[len(fake.getMtimeMappingArgsForCall)] - fake.getMtimeMappingArgsForCall = append(fake.getMtimeMappingArgsForCall, struct { - arg1 string - arg2 string - }{arg1, arg2}) - stub := fake.GetMtimeMappingStub - fakeReturns := fake.getMtimeMappingReturns - fake.recordInvocation("GetMtimeMapping", []interface{}{arg1, arg2}) - fake.getMtimeMappingMutex.Unlock() - if stub != nil { - return stub(arg1, arg2) - } - if specificReturn { - return ret.result1, ret.result2 - } - return fakeReturns.result1, fakeReturns.result2 -} - -func (fake *Model) GetMtimeMappingCallCount() int { - fake.getMtimeMappingMutex.RLock() - defer fake.getMtimeMappingMutex.RUnlock() - return len(fake.getMtimeMappingArgsForCall) -} - -func (fake *Model) GetMtimeMappingCalls(stub func(string, string) (fs.MtimeMapping, error)) { - fake.getMtimeMappingMutex.Lock() - defer fake.getMtimeMappingMutex.Unlock() - fake.GetMtimeMappingStub = stub -} - -func (fake *Model) GetMtimeMappingArgsForCall(i int) (string, string) { - fake.getMtimeMappingMutex.RLock() - defer fake.getMtimeMappingMutex.RUnlock() - argsForCall := fake.getMtimeMappingArgsForCall[i] - return argsForCall.arg1, argsForCall.arg2 -} - -func (fake *Model) GetMtimeMappingReturns(result1 fs.MtimeMapping, result2 error) { - fake.getMtimeMappingMutex.Lock() - defer fake.getMtimeMappingMutex.Unlock() - fake.GetMtimeMappingStub = nil - fake.getMtimeMappingReturns = struct { - result1 fs.MtimeMapping - result2 error - }{result1, result2} -} - -func (fake *Model) GetMtimeMappingReturnsOnCall(i int, result1 fs.MtimeMapping, result2 error) { - fake.getMtimeMappingMutex.Lock() - defer fake.getMtimeMappingMutex.Unlock() - fake.GetMtimeMappingStub = nil - if fake.getMtimeMappingReturnsOnCall == nil { - fake.getMtimeMappingReturnsOnCall = make(map[int]struct { - result1 fs.MtimeMapping - result2 error - }) - } - fake.getMtimeMappingReturnsOnCall[i] = struct { - result1 fs.MtimeMapping - result2 error - }{result1, result2} -} - func (fake *Model) GlobalDirectoryTree(arg1 string, arg2 string, arg3 int, arg4 bool) ([]*model.TreeEntry, error) { fake.globalDirectoryTreeMutex.Lock() ret, specificReturn := fake.globalDirectoryTreeReturnsOnCall[len(fake.globalDirectoryTreeArgsForCall)] @@ -1904,6 +1934,70 @@ func (fake *Model) GlobalDirectoryTreeReturnsOnCall(i int, result1 []*model.Tree }{result1, result2} } +func (fake *Model) GlobalSize(arg1 string) (db.Counts, error) { + fake.globalSizeMutex.Lock() + ret, specificReturn := fake.globalSizeReturnsOnCall[len(fake.globalSizeArgsForCall)] + fake.globalSizeArgsForCall = append(fake.globalSizeArgsForCall, struct { + arg1 string + }{arg1}) + stub := fake.GlobalSizeStub + fakeReturns := fake.globalSizeReturns + fake.recordInvocation("GlobalSize", []interface{}{arg1}) + fake.globalSizeMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *Model) GlobalSizeCallCount() int { + fake.globalSizeMutex.RLock() + defer fake.globalSizeMutex.RUnlock() + return len(fake.globalSizeArgsForCall) +} + +func (fake *Model) GlobalSizeCalls(stub func(string) (db.Counts, error)) { + fake.globalSizeMutex.Lock() + defer fake.globalSizeMutex.Unlock() + fake.GlobalSizeStub = stub +} + +func (fake *Model) GlobalSizeArgsForCall(i int) string { + fake.globalSizeMutex.RLock() + defer fake.globalSizeMutex.RUnlock() + argsForCall := fake.globalSizeArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *Model) GlobalSizeReturns(result1 db.Counts, result2 error) { + fake.globalSizeMutex.Lock() + defer fake.globalSizeMutex.Unlock() + fake.GlobalSizeStub = nil + fake.globalSizeReturns = struct { + result1 db.Counts + result2 error + }{result1, result2} +} + +func (fake *Model) GlobalSizeReturnsOnCall(i int, result1 db.Counts, result2 error) { + fake.globalSizeMutex.Lock() + defer fake.globalSizeMutex.Unlock() + fake.GlobalSizeStub = nil + if fake.globalSizeReturnsOnCall == nil { + fake.globalSizeReturnsOnCall = make(map[int]struct { + result1 db.Counts + result2 error + }) + } + fake.globalSizeReturnsOnCall[i] = struct { + result1 db.Counts + result2 error + }{result1, result2} +} + func (fake *Model) Index(arg1 protocol.Connection, arg2 *protocol.Index) error { fake.indexMutex.Lock() ret, specificReturn := fake.indexReturnsOnCall[len(fake.indexArgsForCall)] @@ -2161,6 +2255,202 @@ func (fake *Model) LocalChangedFolderFilesReturnsOnCall(i int, result1 []protoco }{result1, result2} } +func (fake *Model) LocalFiles(arg1 string, arg2 protocol.DeviceID) (iter.Seq[protocol.FileInfo], func() error) { + fake.localFilesMutex.Lock() + ret, specificReturn := fake.localFilesReturnsOnCall[len(fake.localFilesArgsForCall)] + fake.localFilesArgsForCall = append(fake.localFilesArgsForCall, struct { + arg1 string + arg2 protocol.DeviceID + }{arg1, arg2}) + stub := fake.LocalFilesStub + fakeReturns := fake.localFilesReturns + fake.recordInvocation("LocalFiles", []interface{}{arg1, arg2}) + fake.localFilesMutex.Unlock() + if stub != nil { + return stub(arg1, arg2) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *Model) LocalFilesCallCount() int { + fake.localFilesMutex.RLock() + defer fake.localFilesMutex.RUnlock() + return len(fake.localFilesArgsForCall) +} + +func (fake *Model) LocalFilesCalls(stub func(string, protocol.DeviceID) (iter.Seq[protocol.FileInfo], func() error)) { + fake.localFilesMutex.Lock() + defer fake.localFilesMutex.Unlock() + fake.LocalFilesStub = stub +} + +func (fake *Model) LocalFilesArgsForCall(i int) (string, protocol.DeviceID) { + fake.localFilesMutex.RLock() + defer fake.localFilesMutex.RUnlock() + argsForCall := fake.localFilesArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *Model) LocalFilesReturns(result1 iter.Seq[protocol.FileInfo], result2 func() error) { + fake.localFilesMutex.Lock() + defer fake.localFilesMutex.Unlock() + fake.LocalFilesStub = nil + fake.localFilesReturns = struct { + result1 iter.Seq[protocol.FileInfo] + result2 func() error + }{result1, result2} +} + +func (fake *Model) LocalFilesReturnsOnCall(i int, result1 iter.Seq[protocol.FileInfo], result2 func() error) { + fake.localFilesMutex.Lock() + defer fake.localFilesMutex.Unlock() + fake.LocalFilesStub = nil + if fake.localFilesReturnsOnCall == nil { + fake.localFilesReturnsOnCall = make(map[int]struct { + result1 iter.Seq[protocol.FileInfo] + result2 func() error + }) + } + fake.localFilesReturnsOnCall[i] = struct { + result1 iter.Seq[protocol.FileInfo] + result2 func() error + }{result1, result2} +} + +func (fake *Model) LocalFilesSequenced(arg1 string, arg2 protocol.DeviceID, arg3 int64) (iter.Seq[protocol.FileInfo], func() error) { + fake.localFilesSequencedMutex.Lock() + ret, specificReturn := fake.localFilesSequencedReturnsOnCall[len(fake.localFilesSequencedArgsForCall)] + fake.localFilesSequencedArgsForCall = append(fake.localFilesSequencedArgsForCall, struct { + arg1 string + arg2 protocol.DeviceID + arg3 int64 + }{arg1, arg2, arg3}) + stub := fake.LocalFilesSequencedStub + fakeReturns := fake.localFilesSequencedReturns + fake.recordInvocation("LocalFilesSequenced", []interface{}{arg1, arg2, arg3}) + fake.localFilesSequencedMutex.Unlock() + if stub != nil { + return stub(arg1, arg2, arg3) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *Model) LocalFilesSequencedCallCount() int { + fake.localFilesSequencedMutex.RLock() + defer fake.localFilesSequencedMutex.RUnlock() + return len(fake.localFilesSequencedArgsForCall) +} + +func (fake *Model) LocalFilesSequencedCalls(stub func(string, protocol.DeviceID, int64) (iter.Seq[protocol.FileInfo], func() error)) { + fake.localFilesSequencedMutex.Lock() + defer fake.localFilesSequencedMutex.Unlock() + fake.LocalFilesSequencedStub = stub +} + +func (fake *Model) LocalFilesSequencedArgsForCall(i int) (string, protocol.DeviceID, int64) { + fake.localFilesSequencedMutex.RLock() + defer fake.localFilesSequencedMutex.RUnlock() + argsForCall := fake.localFilesSequencedArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3 +} + +func (fake *Model) LocalFilesSequencedReturns(result1 iter.Seq[protocol.FileInfo], result2 func() error) { + fake.localFilesSequencedMutex.Lock() + defer fake.localFilesSequencedMutex.Unlock() + fake.LocalFilesSequencedStub = nil + fake.localFilesSequencedReturns = struct { + result1 iter.Seq[protocol.FileInfo] + result2 func() error + }{result1, result2} +} + +func (fake *Model) LocalFilesSequencedReturnsOnCall(i int, result1 iter.Seq[protocol.FileInfo], result2 func() error) { + fake.localFilesSequencedMutex.Lock() + defer fake.localFilesSequencedMutex.Unlock() + fake.LocalFilesSequencedStub = nil + if fake.localFilesSequencedReturnsOnCall == nil { + fake.localFilesSequencedReturnsOnCall = make(map[int]struct { + result1 iter.Seq[protocol.FileInfo] + result2 func() error + }) + } + fake.localFilesSequencedReturnsOnCall[i] = struct { + result1 iter.Seq[protocol.FileInfo] + result2 func() error + }{result1, result2} +} + +func (fake *Model) LocalSize(arg1 string, arg2 protocol.DeviceID) (db.Counts, error) { + fake.localSizeMutex.Lock() + ret, specificReturn := fake.localSizeReturnsOnCall[len(fake.localSizeArgsForCall)] + fake.localSizeArgsForCall = append(fake.localSizeArgsForCall, struct { + arg1 string + arg2 protocol.DeviceID + }{arg1, arg2}) + stub := fake.LocalSizeStub + fakeReturns := fake.localSizeReturns + fake.recordInvocation("LocalSize", []interface{}{arg1, arg2}) + fake.localSizeMutex.Unlock() + if stub != nil { + return stub(arg1, arg2) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *Model) LocalSizeCallCount() int { + fake.localSizeMutex.RLock() + defer fake.localSizeMutex.RUnlock() + return len(fake.localSizeArgsForCall) +} + +func (fake *Model) LocalSizeCalls(stub func(string, protocol.DeviceID) (db.Counts, error)) { + fake.localSizeMutex.Lock() + defer fake.localSizeMutex.Unlock() + fake.LocalSizeStub = stub +} + +func (fake *Model) LocalSizeArgsForCall(i int) (string, protocol.DeviceID) { + fake.localSizeMutex.RLock() + defer fake.localSizeMutex.RUnlock() + argsForCall := fake.localSizeArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *Model) LocalSizeReturns(result1 db.Counts, result2 error) { + fake.localSizeMutex.Lock() + defer fake.localSizeMutex.Unlock() + fake.LocalSizeStub = nil + fake.localSizeReturns = struct { + result1 db.Counts + result2 error + }{result1, result2} +} + +func (fake *Model) LocalSizeReturnsOnCall(i int, result1 db.Counts, result2 error) { + fake.localSizeMutex.Lock() + defer fake.localSizeMutex.Unlock() + fake.LocalSizeStub = nil + if fake.localSizeReturnsOnCall == nil { + fake.localSizeReturnsOnCall = make(map[int]struct { + result1 db.Counts + result2 error + }) + } + fake.localSizeReturnsOnCall[i] = struct { + result1 db.Counts + result2 error + }{result1, result2} +} + func (fake *Model) NeedFolderFiles(arg1 string, arg2 int, arg3 int) ([]protocol.FileInfo, []protocol.FileInfo, []protocol.FileInfo, error) { fake.needFolderFilesMutex.Lock() ret, specificReturn := fake.needFolderFilesReturnsOnCall[len(fake.needFolderFilesArgsForCall)] @@ -2233,6 +2523,71 @@ func (fake *Model) NeedFolderFilesReturnsOnCall(i int, result1 []protocol.FileIn }{result1, result2, result3, result4} } +func (fake *Model) NeedSize(arg1 string, arg2 protocol.DeviceID) (db.Counts, error) { + fake.needSizeMutex.Lock() + ret, specificReturn := fake.needSizeReturnsOnCall[len(fake.needSizeArgsForCall)] + fake.needSizeArgsForCall = append(fake.needSizeArgsForCall, struct { + arg1 string + arg2 protocol.DeviceID + }{arg1, arg2}) + stub := fake.NeedSizeStub + fakeReturns := fake.needSizeReturns + fake.recordInvocation("NeedSize", []interface{}{arg1, arg2}) + fake.needSizeMutex.Unlock() + if stub != nil { + return stub(arg1, arg2) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *Model) NeedSizeCallCount() int { + fake.needSizeMutex.RLock() + defer fake.needSizeMutex.RUnlock() + return len(fake.needSizeArgsForCall) +} + +func (fake *Model) NeedSizeCalls(stub func(string, protocol.DeviceID) (db.Counts, error)) { + fake.needSizeMutex.Lock() + defer fake.needSizeMutex.Unlock() + fake.NeedSizeStub = stub +} + +func (fake *Model) NeedSizeArgsForCall(i int) (string, protocol.DeviceID) { + fake.needSizeMutex.RLock() + defer fake.needSizeMutex.RUnlock() + argsForCall := fake.needSizeArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *Model) NeedSizeReturns(result1 db.Counts, result2 error) { + fake.needSizeMutex.Lock() + defer fake.needSizeMutex.Unlock() + fake.NeedSizeStub = nil + fake.needSizeReturns = struct { + result1 db.Counts + result2 error + }{result1, result2} +} + +func (fake *Model) NeedSizeReturnsOnCall(i int, result1 db.Counts, result2 error) { + fake.needSizeMutex.Lock() + defer fake.needSizeMutex.Unlock() + fake.NeedSizeStub = nil + if fake.needSizeReturnsOnCall == nil { + fake.needSizeReturnsOnCall = make(map[int]struct { + result1 db.Counts + result2 error + }) + } + fake.needSizeReturnsOnCall[i] = struct { + result1 db.Counts + result2 error + }{result1, result2} +} + func (fake *Model) OnHello(arg1 protocol.DeviceID, arg2 net.Addr, arg3 protocol.Hello) error { fake.onHelloMutex.Lock() ret, specificReturn := fake.onHelloReturnsOnCall[len(fake.onHelloArgsForCall)] @@ -2448,6 +2803,70 @@ func (fake *Model) PendingFoldersReturnsOnCall(i int, result1 map[string]db.Pend }{result1, result2} } +func (fake *Model) ReceiveOnlySize(arg1 string) (db.Counts, error) { + fake.receiveOnlySizeMutex.Lock() + ret, specificReturn := fake.receiveOnlySizeReturnsOnCall[len(fake.receiveOnlySizeArgsForCall)] + fake.receiveOnlySizeArgsForCall = append(fake.receiveOnlySizeArgsForCall, struct { + arg1 string + }{arg1}) + stub := fake.ReceiveOnlySizeStub + fakeReturns := fake.receiveOnlySizeReturns + fake.recordInvocation("ReceiveOnlySize", []interface{}{arg1}) + fake.receiveOnlySizeMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *Model) ReceiveOnlySizeCallCount() int { + fake.receiveOnlySizeMutex.RLock() + defer fake.receiveOnlySizeMutex.RUnlock() + return len(fake.receiveOnlySizeArgsForCall) +} + +func (fake *Model) ReceiveOnlySizeCalls(stub func(string) (db.Counts, error)) { + fake.receiveOnlySizeMutex.Lock() + defer fake.receiveOnlySizeMutex.Unlock() + fake.ReceiveOnlySizeStub = stub +} + +func (fake *Model) ReceiveOnlySizeArgsForCall(i int) string { + fake.receiveOnlySizeMutex.RLock() + defer fake.receiveOnlySizeMutex.RUnlock() + argsForCall := fake.receiveOnlySizeArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *Model) ReceiveOnlySizeReturns(result1 db.Counts, result2 error) { + fake.receiveOnlySizeMutex.Lock() + defer fake.receiveOnlySizeMutex.Unlock() + fake.ReceiveOnlySizeStub = nil + fake.receiveOnlySizeReturns = struct { + result1 db.Counts + result2 error + }{result1, result2} +} + +func (fake *Model) ReceiveOnlySizeReturnsOnCall(i int, result1 db.Counts, result2 error) { + fake.receiveOnlySizeMutex.Lock() + defer fake.receiveOnlySizeMutex.Unlock() + fake.ReceiveOnlySizeStub = nil + if fake.receiveOnlySizeReturnsOnCall == nil { + fake.receiveOnlySizeReturnsOnCall = make(map[int]struct { + result1 db.Counts + result2 error + }) + } + fake.receiveOnlySizeReturnsOnCall[i] = struct { + result1 db.Counts + result2 error + }{result1, result2} +} + func (fake *Model) RemoteNeedFolderFiles(arg1 string, arg2 protocol.DeviceID, arg3 int, arg4 int) ([]protocol.FileInfo, error) { fake.remoteNeedFolderFilesMutex.Lock() ret, specificReturn := fake.remoteNeedFolderFilesReturnsOnCall[len(fake.remoteNeedFolderFilesArgsForCall)] @@ -2515,6 +2934,70 @@ func (fake *Model) RemoteNeedFolderFilesReturnsOnCall(i int, result1 []protocol. }{result1, result2} } +func (fake *Model) RemoteSequences(arg1 string) (map[protocol.DeviceID]int64, error) { + fake.remoteSequencesMutex.Lock() + ret, specificReturn := fake.remoteSequencesReturnsOnCall[len(fake.remoteSequencesArgsForCall)] + fake.remoteSequencesArgsForCall = append(fake.remoteSequencesArgsForCall, struct { + arg1 string + }{arg1}) + stub := fake.RemoteSequencesStub + fakeReturns := fake.remoteSequencesReturns + fake.recordInvocation("RemoteSequences", []interface{}{arg1}) + fake.remoteSequencesMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *Model) RemoteSequencesCallCount() int { + fake.remoteSequencesMutex.RLock() + defer fake.remoteSequencesMutex.RUnlock() + return len(fake.remoteSequencesArgsForCall) +} + +func (fake *Model) RemoteSequencesCalls(stub func(string) (map[protocol.DeviceID]int64, error)) { + fake.remoteSequencesMutex.Lock() + defer fake.remoteSequencesMutex.Unlock() + fake.RemoteSequencesStub = stub +} + +func (fake *Model) RemoteSequencesArgsForCall(i int) string { + fake.remoteSequencesMutex.RLock() + defer fake.remoteSequencesMutex.RUnlock() + argsForCall := fake.remoteSequencesArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *Model) RemoteSequencesReturns(result1 map[protocol.DeviceID]int64, result2 error) { + fake.remoteSequencesMutex.Lock() + defer fake.remoteSequencesMutex.Unlock() + fake.RemoteSequencesStub = nil + fake.remoteSequencesReturns = struct { + result1 map[protocol.DeviceID]int64 + result2 error + }{result1, result2} +} + +func (fake *Model) RemoteSequencesReturnsOnCall(i int, result1 map[protocol.DeviceID]int64, result2 error) { + fake.remoteSequencesMutex.Lock() + defer fake.remoteSequencesMutex.Unlock() + fake.RemoteSequencesStub = nil + if fake.remoteSequencesReturnsOnCall == nil { + fake.remoteSequencesReturnsOnCall = make(map[int]struct { + result1 map[protocol.DeviceID]int64 + result2 error + }) + } + fake.remoteSequencesReturnsOnCall[i] = struct { + result1 map[protocol.DeviceID]int64 + result2 error + }{result1, result2} +} + func (fake *Model) Request(arg1 protocol.Connection, arg2 *protocol.Request) (protocol.RequestResponse, error) { fake.requestMutex.Lock() ret, specificReturn := fake.requestReturnsOnCall[len(fake.requestArgsForCall)] @@ -2580,7 +3063,7 @@ func (fake *Model) RequestReturnsOnCall(i int, result1 protocol.RequestResponse, }{result1, result2} } -func (fake *Model) RequestGlobal(arg1 context.Context, arg2 protocol.DeviceID, arg3 string, arg4 string, arg5 int, arg6 int64, arg7 int, arg8 []byte, arg9 uint32, arg10 bool) ([]byte, error) { +func (fake *Model) RequestGlobal(arg1 context.Context, arg2 protocol.DeviceID, arg3 string, arg4 string, arg5 int, arg6 int64, arg7 int, arg8 []byte, arg9 bool) ([]byte, error) { var arg8Copy []byte if arg8 != nil { arg8Copy = make([]byte, len(arg8)) @@ -2589,23 +3072,22 @@ func (fake *Model) RequestGlobal(arg1 context.Context, arg2 protocol.DeviceID, a fake.requestGlobalMutex.Lock() ret, specificReturn := fake.requestGlobalReturnsOnCall[len(fake.requestGlobalArgsForCall)] fake.requestGlobalArgsForCall = append(fake.requestGlobalArgsForCall, struct { - arg1 context.Context - arg2 protocol.DeviceID - arg3 string - arg4 string - arg5 int - arg6 int64 - arg7 int - arg8 []byte - arg9 uint32 - arg10 bool - }{arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8Copy, arg9, arg10}) + arg1 context.Context + arg2 protocol.DeviceID + arg3 string + arg4 string + arg5 int + arg6 int64 + arg7 int + arg8 []byte + arg9 bool + }{arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8Copy, arg9}) stub := fake.RequestGlobalStub fakeReturns := fake.requestGlobalReturns - fake.recordInvocation("RequestGlobal", []interface{}{arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8Copy, arg9, arg10}) + fake.recordInvocation("RequestGlobal", []interface{}{arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8Copy, arg9}) fake.requestGlobalMutex.Unlock() if stub != nil { - return stub(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) + return stub(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) } if specificReturn { return ret.result1, ret.result2 @@ -2619,17 +3101,17 @@ func (fake *Model) RequestGlobalCallCount() int { return len(fake.requestGlobalArgsForCall) } -func (fake *Model) RequestGlobalCalls(stub func(context.Context, protocol.DeviceID, string, string, int, int64, int, []byte, uint32, bool) ([]byte, error)) { +func (fake *Model) RequestGlobalCalls(stub func(context.Context, protocol.DeviceID, string, string, int, int64, int, []byte, bool) ([]byte, error)) { fake.requestGlobalMutex.Lock() defer fake.requestGlobalMutex.Unlock() fake.RequestGlobalStub = stub } -func (fake *Model) RequestGlobalArgsForCall(i int) (context.Context, protocol.DeviceID, string, string, int, int64, int, []byte, uint32, bool) { +func (fake *Model) RequestGlobalArgsForCall(i int) (context.Context, protocol.DeviceID, string, string, int, int64, int, []byte, bool) { fake.requestGlobalMutex.RLock() defer fake.requestGlobalMutex.RUnlock() argsForCall := fake.requestGlobalArgsForCall[i] - return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4, argsForCall.arg5, argsForCall.arg6, argsForCall.arg7, argsForCall.arg8, argsForCall.arg9, argsForCall.arg10 + return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4, argsForCall.arg5, argsForCall.arg6, argsForCall.arg7, argsForCall.arg8, argsForCall.arg9 } func (fake *Model) RequestGlobalReturns(result1 []byte, result2 error) { @@ -2997,6 +3479,71 @@ func (fake *Model) ScanFoldersReturnsOnCall(i int, result1 map[string]error) { }{result1} } +func (fake *Model) Sequence(arg1 string, arg2 protocol.DeviceID) (int64, error) { + fake.sequenceMutex.Lock() + ret, specificReturn := fake.sequenceReturnsOnCall[len(fake.sequenceArgsForCall)] + fake.sequenceArgsForCall = append(fake.sequenceArgsForCall, struct { + arg1 string + arg2 protocol.DeviceID + }{arg1, arg2}) + stub := fake.SequenceStub + fakeReturns := fake.sequenceReturns + fake.recordInvocation("Sequence", []interface{}{arg1, arg2}) + fake.sequenceMutex.Unlock() + if stub != nil { + return stub(arg1, arg2) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *Model) SequenceCallCount() int { + fake.sequenceMutex.RLock() + defer fake.sequenceMutex.RUnlock() + return len(fake.sequenceArgsForCall) +} + +func (fake *Model) SequenceCalls(stub func(string, protocol.DeviceID) (int64, error)) { + fake.sequenceMutex.Lock() + defer fake.sequenceMutex.Unlock() + fake.SequenceStub = stub +} + +func (fake *Model) SequenceArgsForCall(i int) (string, protocol.DeviceID) { + fake.sequenceMutex.RLock() + defer fake.sequenceMutex.RUnlock() + argsForCall := fake.sequenceArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *Model) SequenceReturns(result1 int64, result2 error) { + fake.sequenceMutex.Lock() + defer fake.sequenceMutex.Unlock() + fake.SequenceStub = nil + fake.sequenceReturns = struct { + result1 int64 + result2 error + }{result1, result2} +} + +func (fake *Model) SequenceReturnsOnCall(i int, result1 int64, result2 error) { + fake.sequenceMutex.Lock() + defer fake.sequenceMutex.Unlock() + fake.SequenceStub = nil + if fake.sequenceReturnsOnCall == nil { + fake.sequenceReturnsOnCall = make(map[int]struct { + result1 int64 + result2 error + }) + } + fake.sequenceReturnsOnCall[i] = struct { + result1 int64 + result2 error + }{result1, result2} +} + func (fake *Model) Serve(arg1 context.Context) error { fake.serveMutex.Lock() ret, specificReturn := fake.serveReturnsOnCall[len(fake.serveArgsForCall)] @@ -3292,6 +3839,8 @@ func (fake *Model) Invocations() map[string][][]interface{} { defer fake.invocationsMutex.RUnlock() fake.addConnectionMutex.RLock() defer fake.addConnectionMutex.RUnlock() + fake.allGlobalFilesMutex.RLock() + defer fake.allGlobalFilesMutex.RUnlock() fake.availabilityMutex.RLock() defer fake.availabilityMutex.RUnlock() fake.bringToFrontMutex.RLock() @@ -3312,8 +3861,6 @@ func (fake *Model) Invocations() map[string][][]interface{} { defer fake.currentGlobalFileMutex.RUnlock() fake.currentIgnoresMutex.RLock() defer fake.currentIgnoresMutex.RUnlock() - fake.dBSnapshotMutex.RLock() - defer fake.dBSnapshotMutex.RUnlock() fake.delayScanMutex.RLock() defer fake.delayScanMutex.RUnlock() fake.deviceStatisticsMutex.RLock() @@ -3332,10 +3879,10 @@ func (fake *Model) Invocations() map[string][][]interface{} { defer fake.folderStatisticsMutex.RUnlock() fake.getFolderVersionsMutex.RLock() defer fake.getFolderVersionsMutex.RUnlock() - fake.getMtimeMappingMutex.RLock() - defer fake.getMtimeMappingMutex.RUnlock() fake.globalDirectoryTreeMutex.RLock() defer fake.globalDirectoryTreeMutex.RUnlock() + fake.globalSizeMutex.RLock() + defer fake.globalSizeMutex.RUnlock() fake.indexMutex.RLock() defer fake.indexMutex.RUnlock() fake.indexUpdateMutex.RLock() @@ -3344,8 +3891,16 @@ func (fake *Model) Invocations() map[string][][]interface{} { defer fake.loadIgnoresMutex.RUnlock() fake.localChangedFolderFilesMutex.RLock() defer fake.localChangedFolderFilesMutex.RUnlock() + fake.localFilesMutex.RLock() + defer fake.localFilesMutex.RUnlock() + fake.localFilesSequencedMutex.RLock() + defer fake.localFilesSequencedMutex.RUnlock() + fake.localSizeMutex.RLock() + defer fake.localSizeMutex.RUnlock() fake.needFolderFilesMutex.RLock() defer fake.needFolderFilesMutex.RUnlock() + fake.needSizeMutex.RLock() + defer fake.needSizeMutex.RUnlock() fake.onHelloMutex.RLock() defer fake.onHelloMutex.RUnlock() fake.overrideMutex.RLock() @@ -3354,8 +3909,12 @@ func (fake *Model) Invocations() map[string][][]interface{} { defer fake.pendingDevicesMutex.RUnlock() fake.pendingFoldersMutex.RLock() defer fake.pendingFoldersMutex.RUnlock() + fake.receiveOnlySizeMutex.RLock() + defer fake.receiveOnlySizeMutex.RUnlock() fake.remoteNeedFolderFilesMutex.RLock() defer fake.remoteNeedFolderFilesMutex.RUnlock() + fake.remoteSequencesMutex.RLock() + defer fake.remoteSequencesMutex.RUnlock() fake.requestMutex.RLock() defer fake.requestMutex.RUnlock() fake.requestGlobalMutex.RLock() @@ -3372,6 +3931,8 @@ func (fake *Model) Invocations() map[string][][]interface{} { defer fake.scanFolderSubdirsMutex.RUnlock() fake.scanFoldersMutex.RLock() defer fake.scanFoldersMutex.RUnlock() + fake.sequenceMutex.RLock() + defer fake.sequenceMutex.RUnlock() fake.serveMutex.RLock() defer fake.serveMutex.RUnlock() fake.setIgnoresMutex.RLock() diff --git a/lib/model/model.go b/lib/model/model.go index 4a75fee6c..623adae13 100644 --- a/lib/model/model.go +++ b/lib/model/model.go @@ -16,6 +16,7 @@ import ( "errors" "fmt" "io" + "iter" "net" "os" "path/filepath" @@ -29,10 +30,11 @@ import ( "github.com/thejerf/suture/v4" + "github.com/syncthing/syncthing/internal/db" + "github.com/syncthing/syncthing/internal/itererr" "github.com/syncthing/syncthing/lib/build" "github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/connections" - "github.com/syncthing/syncthing/lib/db" "github.com/syncthing/syncthing/lib/events" "github.com/syncthing/syncthing/lib/fs" "github.com/syncthing/syncthing/lib/ignore" @@ -94,7 +96,16 @@ type Model interface { GetFolderVersions(folder string) (map[string][]versioner.FileVersion, error) RestoreFolderVersions(folder string, versions map[string]time.Time) (map[string]error, error) - DBSnapshot(folder string) (*db.Snapshot, error) + LocalFiles(folder string, device protocol.DeviceID) (iter.Seq[protocol.FileInfo], func() error) + LocalFilesSequenced(folder string, device protocol.DeviceID, startSet int64) (iter.Seq[protocol.FileInfo], func() error) + LocalSize(folder string, device protocol.DeviceID) (db.Counts, error) + GlobalSize(folder string) (db.Counts, error) + NeedSize(folder string, device protocol.DeviceID) (db.Counts, error) + ReceiveOnlySize(folder string) (db.Counts, error) + Sequence(folder string, device protocol.DeviceID) (int64, error) + AllGlobalFiles(folder string) (iter.Seq[db.FileMetadata], func() error) + RemoteSequences(folder string) (map[protocol.DeviceID]int64, error) + NeedFolderFiles(folder string, page, perpage int) ([]protocol.FileInfo, []protocol.FileInfo, []protocol.FileInfo, error) RemoteNeedFolderFiles(folder string, device protocol.DeviceID, page, perpage int) ([]protocol.FileInfo, error) LocalChangedFolderFiles(folder string, page, perpage int) ([]protocol.FileInfo, error) @@ -102,7 +113,6 @@ type Model interface { CurrentFolderFile(folder string, file string) (protocol.FileInfo, bool, error) CurrentGlobalFile(folder string, file string) (protocol.FileInfo, bool, error) - GetMtimeMapping(folder string, file string) (fs.MtimeMapping, error) Availability(folder string, file protocol.FileInfo, block protocol.BlockInfo) ([]Availability, error) Completion(device protocol.DeviceID, folder string) (FolderCompletion, error) @@ -119,7 +129,7 @@ type Model interface { GlobalDirectoryTree(folder, prefix string, levels int, dirsOnly bool) ([]*TreeEntry, error) - RequestGlobal(ctx context.Context, deviceID protocol.DeviceID, folder, name string, blockNo int, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error) + RequestGlobal(ctx context.Context, deviceID protocol.DeviceID, folder, name string, blockNo int, offset int64, size int, hash []byte, fromTemporary bool) ([]byte, error) } type model struct { @@ -128,12 +138,11 @@ type model struct { // constructor parameters cfg config.Wrapper id protocol.DeviceID - db *db.Lowlevel + sdb db.DB protectedFiles []string evLogger events.Logger // constant or concurrency safe fields - finder *db.BlockFinder progressEmitter *ProgressEmitter shortID protocol.ShortID // globalRequestLimiter limits the amount of data in concurrent incoming @@ -146,11 +155,11 @@ type model struct { started chan struct{} keyGen *protocol.KeyGenerator promotionTimer *time.Timer + observed *db.ObservedDB // fields protected by mut mut sync.RWMutex folderCfgs map[string]config.FolderConfiguration // folder -> cfg - folderFiles map[string]*db.FileSet // folder -> files deviceStatRefs map[protocol.DeviceID]*stats.DeviceStatisticsReference // deviceID -> statsRef folderIgnores map[string]*ignore.Matcher // folder -> matcher object folderRunners *serviceMap[string, service] // folder -> puller or scanner @@ -174,7 +183,7 @@ type model struct { var _ config.Verifier = &model{} -type folderFactory func(*model, *db.FileSet, *ignore.Matcher, config.FolderConfiguration, versioner.Versioner, events.Logger, *semaphore.Semaphore) service +type folderFactory func(*model, *ignore.Matcher, config.FolderConfiguration, versioner.Versioner, events.Logger, *semaphore.Semaphore) service var folderFactories = make(map[config.FolderType]folderFactory) @@ -186,7 +195,7 @@ var ( ErrFolderMissing = errors.New("no such folder") errNoVersioner = errors.New("folder has no versioner") // errors about why a connection is closed - errStopped = errors.New("Syncthing is being stopped") + errStopped = errors.New("Syncthing is being stopped") //nolint:staticcheck errEncryptionInvConfigLocal = errors.New("can't encrypt outgoing data because local data is encrypted (folder-type receive-encrypted)") errEncryptionInvConfigRemote = errors.New("remote has encrypted data and encrypts that data for us - this is impossible") errEncryptionNotEncryptedLocal = errors.New("remote expects to exchange encrypted data, but is configured for plain data") @@ -203,7 +212,7 @@ var ( // NewModel creates and starts a new model. The model starts in read-only mode, // where it sends index information to connected peers and responds to requests // for file data without altering the local folder in any way. -func NewModel(cfg config.Wrapper, id protocol.DeviceID, ldb *db.Lowlevel, protectedFiles []string, evLogger events.Logger, keyGen *protocol.KeyGenerator) Model { +func NewModel(cfg config.Wrapper, id protocol.DeviceID, sdb db.DB, protectedFiles []string, evLogger events.Logger, keyGen *protocol.KeyGenerator) Model { spec := svcutil.SpecWithDebugLogger(l) m := &model{ Supervisor: suture.New("model", spec), @@ -211,12 +220,11 @@ func NewModel(cfg config.Wrapper, id protocol.DeviceID, ldb *db.Lowlevel, protec // constructor parameters cfg: cfg, id: id, - db: ldb, + sdb: sdb, protectedFiles: protectedFiles, evLogger: evLogger, // constant or concurrency safe fields - finder: db.NewBlockFinder(ldb), progressEmitter: NewProgressEmitter(cfg, evLogger), shortID: id.Short(), globalRequestLimiter: semaphore.New(1024 * cfg.Options().MaxConcurrentIncomingRequestKiB()), @@ -225,11 +233,11 @@ func NewModel(cfg config.Wrapper, id protocol.DeviceID, ldb *db.Lowlevel, protec started: make(chan struct{}), keyGen: keyGen, promotionTimer: time.NewTimer(0), + observed: db.NewObservedDB(sdb), // fields protected by mut mut: sync.NewRWMutex(), folderCfgs: make(map[string]config.FolderConfiguration), - folderFiles: make(map[string]*db.FileSet), deviceStatRefs: make(map[protocol.DeviceID]*stats.DeviceStatisticsReference), folderIgnores: make(map[string]*ignore.Matcher), folderRunners: newServiceMap[string, service](evLogger), @@ -247,7 +255,7 @@ func NewModel(cfg config.Wrapper, id protocol.DeviceID, ldb *db.Lowlevel, protec indexHandlers: newServiceMap[protocol.DeviceID, *indexHandlerRegistry](evLogger), } for devID, cfg := range cfg.Devices() { - m.deviceStatRefs[devID] = stats.NewDeviceStatisticsReference(m.db, devID) + m.deviceStatRefs[devID] = stats.NewDeviceStatisticsReference(db.NewTyped(sdb, "devicestats/"+devID.String())) m.setConnRequestLimitersLocked(cfg) } m.Add(m.folderRunners) @@ -328,21 +336,20 @@ func (m *model) fatal(err error) { } // Need to hold lock on m.mut when calling this. -func (m *model) addAndStartFolderLocked(cfg config.FolderConfiguration, fset *db.FileSet, cacheIgnoredFiles bool) { - ignores := ignore.New(cfg.Filesystem(nil), ignore.WithCache(cacheIgnoredFiles)) +func (m *model) addAndStartFolderLocked(cfg config.FolderConfiguration, cacheIgnoredFiles bool) { + ignores := ignore.New(cfg.Filesystem(), ignore.WithCache(cacheIgnoredFiles)) if cfg.Type != config.FolderTypeReceiveEncrypted { if err := ignores.Load(".stignore"); err != nil && !fs.IsNotExist(err) { l.Warnln("Loading ignores:", err) } } - m.addAndStartFolderLockedWithIgnores(cfg, fset, ignores) + m.addAndStartFolderLockedWithIgnores(cfg, ignores) } // Only needed for testing, use addAndStartFolderLocked instead. -func (m *model) addAndStartFolderLockedWithIgnores(cfg config.FolderConfiguration, fset *db.FileSet, ignores *ignore.Matcher) { +func (m *model) addAndStartFolderLockedWithIgnores(cfg config.FolderConfiguration, ignores *ignore.Matcher) { m.folderCfgs[cfg.ID] = cfg - m.folderFiles[cfg.ID] = fset m.folderIgnores[cfg.ID] = ignores _, ok := m.folderRunners.Get(cfg.ID) @@ -361,16 +368,19 @@ func (m *model) addAndStartFolderLockedWithIgnores(cfg config.FolderConfiguratio // Find any devices for which we hold the index in the db, but the folder // is not shared, and drop it. expected := mapDevices(cfg.DeviceIDs()) - for _, available := range fset.ListDevices() { + devs, _ := m.sdb.ListDevicesForFolder(cfg.ID) + for _, available := range devs { if _, ok := expected[available]; !ok { l.Debugln("dropping", folder, "state for", available) - fset.Drop(available) + _ = m.sdb.DropAllFiles(folder, available) } } - v, ok := fset.Sequence(protocol.LocalDeviceID), true - indexHasFiles := ok && v > 0 - if !indexHasFiles { + seq, err := m.sdb.GetDeviceSequence(folder, protocol.LocalDeviceID) + if err != nil { + panic(fmt.Errorf("error getting sequence number: %w", err)) + } + if seq == 0 { // It's a blank folder, so this may the first time we're looking at // it. Attempt to create and tag with our marker as appropriate. We // don't really do anything with errors at this point except warn - @@ -393,7 +403,7 @@ func (m *model) addAndStartFolderLockedWithIgnores(cfg config.FolderConfiguratio } // These are our metadata files, and they should always be hidden. - ffs := cfg.Filesystem(nil) + ffs := cfg.Filesystem() _ = ffs.Hide(config.DefaultMarkerName) _ = ffs.Hide(versioner.DefaultPath) _ = ffs.Hide(".stignore") @@ -410,7 +420,7 @@ func (m *model) addAndStartFolderLockedWithIgnores(cfg config.FolderConfiguratio m.warnAboutOverwritingProtectedFiles(cfg, ignores) - p := folderFactory(m, fset, ignores, cfg, ver, m.evLogger, m.folderIOLimiter) + p := folderFactory(m, ignores, cfg, ver, m.evLogger, m.folderIOLimiter) m.folderRunners.Add(folder, p) l.Infof("Ready to synchronize %s (%s)", cfg.Description(), cfg.Type) @@ -422,7 +432,7 @@ func (m *model) warnAboutOverwritingProtectedFiles(cfg config.FolderConfiguratio } // This is a bit of a hack. - ffs := cfg.Filesystem(nil) + ffs := cfg.Filesystem() if ffs.Type() != fs.FilesystemTypeBasic { return } @@ -469,7 +479,7 @@ func (m *model) removeFolder(cfg config.FolderConfiguration) { // otherwise not removable) Syncthing-specific marker files. if err := cfg.RemoveMarker(); err != nil && !errors.Is(err, os.ErrNotExist) { moved := config.DefaultMarkerName + time.Now().Format(".removed-20060102-150405") - fs := cfg.Filesystem(nil) + fs := cfg.Filesystem() _ = fs.Rename(config.DefaultMarkerName, moved) } } @@ -483,7 +493,7 @@ func (m *model) removeFolder(cfg config.FolderConfiguration) { m.mut.Unlock() // Remove it from the database - db.DropFolder(m.db, cfg.ID) + _ = m.sdb.DropFolder(cfg.ID) } // Need to hold lock on m.mut when calling this. @@ -491,7 +501,6 @@ func (m *model) cleanupFolderLocked(cfg config.FolderConfiguration) { // clear up our config maps m.folderRunners.Remove(cfg.ID) delete(m.folderCfgs, cfg.ID) - delete(m.folderFiles, cfg.ID) delete(m.folderIgnores, cfg.ID) delete(m.folderVersioners, cfg.ID) delete(m.folderEncryptionPasswordTokens, cfg.ID) @@ -526,28 +535,14 @@ func (m *model) restartFolder(from, to config.FolderConfiguration, cacheIgnoredF m.mut.Lock() defer m.mut.Unlock() - // Cache the (maybe) existing fset before it's removed by cleanupFolderLocked - fset := m.folderFiles[folder] - fsetNil := fset == nil - m.cleanupFolderLocked(from) if !to.Paused { - if fsetNil { - // Create a new fset. Might take a while and we do it under - // locking, but it's unsafe to create fset:s concurrently so - // that's the price we pay. - var err error - fset, err = db.NewFileSet(folder, m.db) - if err != nil { - return fmt.Errorf("restarting %v: %w", to.Description(), err) - } - } - m.addAndStartFolderLocked(to, fset, cacheIgnoredFiles) + m.addAndStartFolderLocked(to, cacheIgnoredFiles) } runner, _ := m.folderRunners.Get(to.ID) m.indexHandlers.Each(func(_ protocol.DeviceID, r *indexHandlerRegistry) error { - r.RegisterFolderState(to, fset, runner) + r.RegisterFolderState(to, runner) return nil }) @@ -569,22 +564,14 @@ func (m *model) newFolder(cfg config.FolderConfiguration, cacheIgnoredFiles bool m.mut.Lock() defer m.mut.Unlock() - // Creating the fileset can take a long time (metadata calculation), but - // nevertheless should happen inside the lock (same as when restarting - // a folder). - fset, err := db.NewFileSet(cfg.ID, m.db) - if err != nil { - return fmt.Errorf("adding %v: %w", cfg.Description(), err) - } - - m.addAndStartFolderLocked(cfg, fset, cacheIgnoredFiles) + m.addAndStartFolderLocked(cfg, cacheIgnoredFiles) // Cluster configs might be received and processed before reaching this // point, i.e. before the folder is started. If that's the case, start // index senders here. m.indexHandlers.Each(func(_ protocol.DeviceID, r *indexHandlerRegistry) error { runner, _ := m.folderRunners.Get(cfg.ID) - r.RegisterFolderState(cfg, fset, runner) + r.RegisterFolderState(cfg, runner) return nil }) @@ -607,8 +594,6 @@ func (m *model) UsageReportingStats(report *contract.Report, version int, previe report.BlockStats.Pulled = v case "copyOrigin": report.BlockStats.CopyOrigin = v - case "copyOriginShifted": - report.BlockStats.CopyOriginShifted = v case "copyElsewhere": report.BlockStats.CopyElsewhere = v } @@ -637,24 +622,26 @@ func (m *model) UsageReportingStats(report *contract.Report, version int, previe for _, line := range lines { // Allow prefixes to be specified in any order, but only once. + loop: for { - if strings.HasPrefix(line, "!") && !seenPrefix[0] { + switch { + case strings.HasPrefix(line, "!") && !seenPrefix[0]: seenPrefix[0] = true line = line[1:] report.IgnoreStats.Inverts++ - } else if strings.HasPrefix(line, "(?i)") && !seenPrefix[1] { + case strings.HasPrefix(line, "(?i)") && !seenPrefix[1]: seenPrefix[1] = true line = line[4:] report.IgnoreStats.Folded++ - } else if strings.HasPrefix(line, "(?d)") && !seenPrefix[2] { + case strings.HasPrefix(line, "(?d)") && !seenPrefix[2]: seenPrefix[2] = true line = line[4:] report.IgnoreStats.Deletable++ - } else { + default: seenPrefix[0] = false seenPrefix[1] = false seenPrefix[2] = false - break + break loop } } @@ -926,46 +913,78 @@ func (m *model) Completion(device protocol.DeviceID, folder string) (FolderCompl func (m *model) folderCompletion(device protocol.DeviceID, folder string) (FolderCompletion, error) { m.mut.RLock() err := m.checkFolderRunningRLocked(folder) - rf := m.folderFiles[folder] m.mut.RUnlock() if err != nil { return FolderCompletion{}, err } - snap, err := rf.Snapshot() - if err != nil { - return FolderCompletion{}, err - } - defer snap.Release() - m.mut.RLock() state := m.remoteFolderStates[device][folder] downloaded := m.deviceDownloads[device].BytesDownloaded(folder) m.mut.RUnlock() - need := snap.NeedSize(device) + need, err := m.sdb.CountNeed(folder, device) + if err != nil { + return FolderCompletion{}, err + } need.Bytes -= downloaded // This might be more than it really is, because some blocks can be of a smaller size. if need.Bytes < 0 { need.Bytes = 0 } - comp := newFolderCompletion(snap.GlobalSize(), need, snap.Sequence(device), state) + seq, err := m.sdb.GetDeviceSequence(folder, device) + if err != nil { + return FolderCompletion{}, err + } + glob, err := m.sdb.CountGlobal(folder) + if err != nil { + return FolderCompletion{}, err + } + comp := newFolderCompletion(glob, need, seq, state) l.Debugf("%v Completion(%s, %q): %v", m, device, folder, comp.Map()) return comp, nil } -// DBSnapshot returns a snapshot of the database content relevant to the given folder. -func (m *model) DBSnapshot(folder string) (*db.Snapshot, error) { - m.mut.RLock() - err := m.checkFolderRunningRLocked(folder) - rf := m.folderFiles[folder] - m.mut.RUnlock() - if err != nil { - return nil, err - } - return rf.Snapshot() +func (m *model) LocalFiles(folder string, device protocol.DeviceID) (iter.Seq[protocol.FileInfo], func() error) { + return m.sdb.AllLocalFiles(folder, device) +} + +func (m *model) LocalFilesSequenced(folder string, device protocol.DeviceID, startSeq int64) (iter.Seq[protocol.FileInfo], func() error) { + return m.sdb.AllLocalFilesBySequence(folder, device, startSeq, 0) +} + +func (m *model) AllForBlocksHash(folder string, h []byte) (iter.Seq[db.FileMetadata], func() error) { + return m.sdb.AllLocalFilesWithBlocksHash(folder, h) +} + +func (m *model) LocalSize(folder string, device protocol.DeviceID) (db.Counts, error) { + return m.sdb.CountLocal(folder, device) +} + +func (m *model) GlobalSize(folder string) (db.Counts, error) { + return m.sdb.CountGlobal(folder) +} + +func (m *model) NeedSize(folder string, device protocol.DeviceID) (db.Counts, error) { + return m.sdb.CountNeed(folder, device) +} + +func (m *model) ReceiveOnlySize(folder string) (db.Counts, error) { + return m.sdb.CountReceiveOnlyChanged(folder) +} + +func (m *model) Sequence(folder string, device protocol.DeviceID) (int64, error) { + return m.sdb.GetDeviceSequence(folder, device) +} + +func (m *model) AllGlobalFiles(folder string) (iter.Seq[db.FileMetadata], func() error) { + return m.sdb.AllGlobalFiles(folder) +} + +func (m *model) RemoteSequences(folder string) (map[protocol.DeviceID]int64, error) { + return m.sdb.RemoteSequences(folder) } func (m *model) FolderProgressBytesCompleted(folder string) int64 { @@ -976,20 +995,14 @@ func (m *model) FolderProgressBytesCompleted(folder string) int64 { // progress, queued, and to be queued on next puller iteration. func (m *model) NeedFolderFiles(folder string, page, perpage int) ([]protocol.FileInfo, []protocol.FileInfo, []protocol.FileInfo, error) { m.mut.RLock() - rf, rfOk := m.folderFiles[folder] runner, runnerOk := m.folderRunners.Get(folder) - cfg := m.folderCfgs[folder] + cfg, cfgOK := m.folderCfgs[folder] m.mut.RUnlock() - if !rfOk { + if !cfgOK { return nil, nil, nil, ErrFolderMissing } - snap, err := rf.Snapshot() - if err != nil { - return nil, nil, nil, err - } - defer snap.Release() var progress, queued, rest []protocol.FileInfo var seen map[string]struct{} @@ -1003,14 +1016,14 @@ func (m *model) NeedFolderFiles(folder string, page, perpage int) ([]protocol.Fi seen = make(map[string]struct{}, len(progressNames)+len(queuedNames)) for i, name := range progressNames { - if f, ok := snap.GetGlobalTruncated(name); ok { + if f, ok, err := m.sdb.GetGlobalFile(folder, name); err == nil && ok { progress[i] = f seen[name] = struct{}{} } } for i, name := range queuedNames { - if f, ok := snap.GetGlobalTruncated(name); ok { + if f, ok, err := m.sdb.GetGlobalFile(folder, name); err == nil && ok { queued[i] = f seen[name] = struct{}{} } @@ -1023,21 +1036,29 @@ func (m *model) NeedFolderFiles(folder string, page, perpage int) ([]protocol.Fi p.toSkip -= skipped } - rest = make([]protocol.FileInfo, 0, perpage) - snap.WithNeedTruncated(protocol.LocalDeviceID, func(f protocol.FileInfo) bool { - if cfg.IgnoreDelete && f.IsDeleted() { - return true - } + if p.get > 0 { + rest = make([]protocol.FileInfo, 0, p.get) + it, errFn := m.sdb.AllNeededGlobalFiles(folder, protocol.LocalDeviceID, config.PullOrderAlphabetic, 0, 0) + for f := range it { + if cfg.IgnoreDelete && f.IsDeleted() { + continue + } - if p.skip() { - return true + if p.skip() { + continue + } + if _, ok := seen[f.Name]; !ok { + rest = append(rest, f) + p.get-- + } + if p.get == 0 { + break + } } - if _, ok := seen[f.Name]; !ok { - rest = append(rest, f) - p.get-- + if err := errFn(); err != nil { + return nil, nil, nil, err } - return p.get > 0 - }) + } return progress, queued, rest, nil } @@ -1046,63 +1067,56 @@ func (m *model) NeedFolderFiles(folder string, page, perpage int) ([]protocol.Fi // remote device to become synced with a folder. func (m *model) RemoteNeedFolderFiles(folder string, device protocol.DeviceID, page, perpage int) ([]protocol.FileInfo, error) { m.mut.RLock() - rf, ok := m.folderFiles[folder] + _, ok := m.folderCfgs[folder] m.mut.RUnlock() if !ok { return nil, ErrFolderMissing } - snap, err := rf.Snapshot() - if err != nil { - return nil, err - } - defer snap.Release() - - files := make([]protocol.FileInfo, 0, perpage) - p := newPager(page, perpage) - snap.WithNeedTruncated(device, func(f protocol.FileInfo) bool { - if p.skip() { - return true - } - files = append(files, f) - return !p.done() - }) - return files, nil + it, errFn := m.sdb.AllNeededGlobalFiles(folder, device, config.PullOrderAlphabetic, perpage, (page-1)*perpage) + files := slices.Collect(it) + return files, errFn() } func (m *model) LocalChangedFolderFiles(folder string, page, perpage int) ([]protocol.FileInfo, error) { m.mut.RLock() - rf, ok := m.folderFiles[folder] + _, ok := m.folderCfgs[folder] m.mut.RUnlock() if !ok { return nil, ErrFolderMissing } - snap, err := rf.Snapshot() + ros, err := m.sdb.CountReceiveOnlyChanged(folder) if err != nil { return nil, err } - defer snap.Release() - - if snap.ReceiveOnlyChangedSize().TotalItems() == 0 { + if ros.TotalItems() == 0 { return nil, nil } p := newPager(page, perpage) files := make([]protocol.FileInfo, 0, perpage) - snap.WithHaveTruncated(protocol.LocalDeviceID, func(f protocol.FileInfo) bool { + // This could be made more efficient with a specifically targeted DB + // call + it, errFn := m.sdb.AllLocalFiles(folder, protocol.LocalDeviceID) + for f := range it { if !f.IsReceiveOnlyChanged() { - return true + continue } if p.skip() { - return true + continue } files = append(files, f) - return !p.done() - }) + if p.done() { + break + } + } + if err := errFn(); err != nil { + return nil, err + } return files, nil } @@ -1215,9 +1229,10 @@ func (m *model) ClusterConfig(conn protocol.Connection, cm *protocol.ClusterConf for _, folder := range cm.Folders { info := &clusterConfigDeviceInfo{} for _, dev := range folder.Devices { - if dev.ID == m.id { + switch dev.ID { + case m.id: info.local = dev - } else if dev.ID == deviceID { + case deviceID: info.remote = dev } if info.local.ID != protocol.EmptyDeviceID && info.remote.ID != protocol.EmptyDeviceID { @@ -1346,11 +1361,11 @@ func (m *model) ensureIndexHandler(conn protocol.Connection) *indexHandlerRegist } // Create a new index handler for this device. - indexHandlerRegistry = newIndexHandlerRegistry(conn, m.deviceDownloads[deviceID], m.evLogger) + indexHandlerRegistry = newIndexHandlerRegistry(conn, m.sdb, m.deviceDownloads[deviceID], m.evLogger) for id, fcfg := range m.folderCfgs { l.Debugln("Registering folder", id, "for", deviceID.Short()) runner, _ := m.folderRunners.Get(id) - indexHandlerRegistry.RegisterFolderState(fcfg, m.folderFiles[id], runner) + indexHandlerRegistry.RegisterFolderState(fcfg, runner) } m.indexHandlers.Add(deviceID, indexHandlerRegistry) @@ -1379,7 +1394,7 @@ func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.Devi seenFolders := make(map[string]remoteFolderState, len(folders)) updatedPending := make([]updatedPendingFolder, 0, len(folders)) deviceID := deviceCfg.DeviceID - expiredPending, err := m.db.PendingFoldersForDevice(deviceID) + expiredPending, err := m.observed.PendingFoldersForDevice(deviceID) if err != nil { l.Infof("Could not get pending folders for cleanup: %v", err) } @@ -1401,7 +1416,7 @@ func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.Devi of.Label = folder.Label of.ReceiveEncrypted = len(ccDeviceInfos[folder.ID].local.EncryptionPasswordToken) > 0 of.RemoteEncrypted = len(ccDeviceInfos[folder.ID].remote.EncryptionPasswordToken) > 0 - if err := m.db.AddOrUpdatePendingFolder(folder.ID, of, deviceID); err != nil { + if err := m.observed.AddOrUpdatePendingFolder(folder.ID, of, deviceID); err != nil { l.Warnf("Failed to persist pending folder entry to database: %v", err) } if !folder.Paused { @@ -1439,7 +1454,7 @@ func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.Devi sameError := false m.mut.Lock() if devs, ok := m.folderEncryptionFailures[folder.ID]; ok { - sameError = devs[deviceID] == err + sameError = devs[deviceID] == err //nolint:errorlint } else { m.folderEncryptionFailures[folder.ID] = make(map[protocol.DeviceID]error) } @@ -1449,7 +1464,8 @@ func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.Devi if sameError { l.Debugln(msg) } else { - if rerr, ok := err.(*redactedError); ok { + var rerr *redactedError + if errors.As(err, &rerr) { err = rerr.redacted } m.evLogger.Log(events.Failure, err.Error()) @@ -1488,7 +1504,7 @@ func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.Devi expiredPendingList := make([]map[string]string, 0, len(expiredPending)) for folder := range expiredPending { - if err = m.db.RemovePendingFolderForDevice(folder, deviceID); err != nil { + if err = m.observed.RemovePendingFolderForDevice(folder, deviceID); err != nil { msg := "Failed to remove pending folder-device entry" l.Warnf("%v (%v, %v): %v", msg, folder, deviceID, err) m.evLogger.Log(events.Failure, msg) @@ -1547,7 +1563,7 @@ func (m *model) ccCheckEncryption(fcfg config.FolderConfiguration, folderDevice if isEncryptedRemote { passwordToken := protocol.PasswordToken(m.keyGen, fcfg.ID, folderDevice.EncryptionPassword) - match := false + var match bool if hasTokenLocal { match = bytes.Equal(passwordToken, ccDeviceInfos.local.EncryptionPasswordToken) } else { @@ -2003,7 +2019,7 @@ func (m *model) Request(conn protocol.Connection, req *protocol.Request) (out pr // The requestResponse releases the bytes to the buffer pool and the // limiters when its Close method is called. - res := newLimitedRequestResponse(int(req.Size), limiter, m.globalRequestLimiter) + res := newLimitedRequestResponse(req.Size, limiter, m.globalRequestLimiter) defer func() { // Close it ourselves if it isn't returned due to an error @@ -2015,7 +2031,7 @@ func (m *model) Request(conn protocol.Connection, req *protocol.Request) (out pr // Grab the FS after limiting, as it causes I/O and we want to minimize // the race time between the symlink check and the read. - folderFs := folderCfg.Filesystem(nil) + folderFs := folderCfg.Filesystem() if err := osutil.TraversesSymlink(folderFs, filepath.Dir(req.Name)); err != nil { l.Debugf("%v REQ(in) traversal check: %s - %s: %q / %q o=%d s=%d", m, err, deviceID.Short(), req.Folder, req.Name, req.Offset, req.Size) @@ -2034,7 +2050,7 @@ func (m *model) Request(conn protocol.Connection, req *protocol.Request) (out pr return nil, protocol.ErrNoSuchFile } _, err := readOffsetIntoBuf(folderFs, tempFn, req.Offset, res.data) - if err == nil && scanner.Validate(res.data, req.Hash, req.WeakHash) { + if err == nil && scanner.Validate(res.data, req.Hash) { return res, nil } // Fall through to reading from a non-temp file, just in case the temp @@ -2049,22 +2065,23 @@ func (m *model) Request(conn protocol.Connection, req *protocol.Request) (out pr } n, err := readOffsetIntoBuf(folderFs, req.Name, req.Offset, res.data) - if fs.IsNotExist(err) { + switch { + case fs.IsNotExist(err): l.Debugf("%v REQ(in) file doesn't exist: %s: %q / %q o=%d s=%d", m, deviceID.Short(), req.Folder, req.Name, req.Offset, req.Size) return nil, protocol.ErrNoSuchFile - } else if err == io.EOF { + case errors.Is(err, io.EOF): // Read beyond end of file. This might indicate a problem, or it // might be a short block that gets padded when read for encrypted // folders. We ignore the error and let the hash validation in the // next step take care of it, by only hashing the part we actually // managed to read. - } else if err != nil { + case err != nil: l.Debugf("%v REQ(in) failed reading file (%v): %s: %q / %q o=%d s=%d", m, err, deviceID.Short(), req.Folder, req.Name, req.Offset, req.Size) return nil, protocol.ErrGeneric } - if folderCfg.Type != config.FolderTypeReceiveEncrypted && len(req.Hash) > 0 && !scanner.Validate(res.data[:n], req.Hash, req.WeakHash) { - m.recheckFile(deviceID, req.Folder, req.Name, req.Offset, req.Hash, req.WeakHash) + if folderCfg.Type != config.FolderTypeReceiveEncrypted && len(req.Hash) > 0 && !scanner.Validate(res.data[:n], req.Hash) { + m.recheckFile(deviceID, req.Folder, req.Name, req.Offset, req.Hash) l.Debugf("%v REQ(in) failed validating data: %s: %q / %q o=%d s=%d", m, deviceID.Short(), req.Folder, req.Name, req.Offset, req.Size) return nil, protocol.ErrNoSuchFile } @@ -2090,7 +2107,7 @@ func newLimitedRequestResponse(size int, limiters ...*semaphore.Semaphore) *requ return res } -func (m *model) recheckFile(deviceID protocol.DeviceID, folder, name string, offset int64, hash []byte, weakHash uint32) { +func (m *model) recheckFile(deviceID protocol.DeviceID, folder, name string, offset int64, hash []byte) { cf, ok, err := m.CurrentFolderFile(folder, name) if err != nil { l.Debugf("%v recheckFile: %s: %q / %q: current file error: %v", m, deviceID, folder, name, err) @@ -2119,10 +2136,6 @@ func (m *model) recheckFile(deviceID protocol.DeviceID, folder, name string, off l.Debugf("%v recheckFile: %s: %q / %q i=%d: hash mismatch %x != %x", m, deviceID, folder, name, blockIndex, block.Hash, hash) return } - if weakHash != 0 && block.WeakHash != weakHash { - l.Debugf("%v recheckFile: %s: %q / %q i=%d: weak hash mismatch %v != %v", m, deviceID, folder, name, blockIndex, block.WeakHash, weakHash) - return - } // The hashes provided part of the request match what we expect to find according // to what we have in the database, yet the content we've read off the filesystem doesn't @@ -2142,46 +2155,11 @@ func (m *model) recheckFile(deviceID protocol.DeviceID, folder, name string, off } func (m *model) CurrentFolderFile(folder string, file string) (protocol.FileInfo, bool, error) { - m.mut.RLock() - fs, ok := m.folderFiles[folder] - m.mut.RUnlock() - if !ok { - return protocol.FileInfo{}, false, ErrFolderMissing - } - snap, err := fs.Snapshot() - if err != nil { - return protocol.FileInfo{}, false, err - } - f, ok := snap.Get(protocol.LocalDeviceID, file) - snap.Release() - return f, ok, nil + return m.sdb.GetDeviceFile(folder, protocol.LocalDeviceID, file) } func (m *model) CurrentGlobalFile(folder string, file string) (protocol.FileInfo, bool, error) { - m.mut.RLock() - ffs, ok := m.folderFiles[folder] - m.mut.RUnlock() - if !ok { - return protocol.FileInfo{}, false, ErrFolderMissing - } - snap, err := ffs.Snapshot() - if err != nil { - return protocol.FileInfo{}, false, err - } - f, ok := snap.GetGlobal(file) - snap.Release() - return f, ok, nil -} - -func (m *model) GetMtimeMapping(folder string, file string) (fs.MtimeMapping, error) { - m.mut.RLock() - ffs, ok := m.folderFiles[folder] - fcfg := m.folderCfgs[folder] - m.mut.RUnlock() - if !ok { - return fs.MtimeMapping{}, ErrFolderMissing - } - return fs.GetMtimeMapping(fcfg.Filesystem(ffs), file) + return m.sdb.GetGlobalFile(folder, file) } // Connection returns if we are connected to the given device. @@ -2212,7 +2190,7 @@ func (m *model) LoadIgnores(folder string) ([]string, []string, error) { } if !ignoresOk { - ignores = ignore.New(cfg.Filesystem(nil)) + ignores = ignore.New(cfg.Filesystem()) } err := ignores.Load(".stignore") @@ -2257,17 +2235,17 @@ func (m *model) SetIgnores(folder string, content []string) error { func (m *model) setIgnores(cfg config.FolderConfiguration, content []string) error { err := cfg.CheckPath() - if err == config.ErrPathMissing { + if errors.Is(err, config.ErrPathMissing) { if err = cfg.CreateRoot(); err != nil { return fmt.Errorf("failed to create folder root: %w", err) } err = cfg.CheckPath() } - if err != nil && err != config.ErrMarkerMissing { + if err != nil && !errors.Is(err, config.ErrMarkerMissing) { return err } - if err := ignore.WriteIgnores(cfg.Filesystem(nil), ".stignore", content); err != nil { + if err := ignore.WriteIgnores(cfg.Filesystem(), ".stignore", content); err != nil { l.Warnln("Saving .stignore:", err) return err } @@ -2286,7 +2264,7 @@ func (m *model) setIgnores(cfg config.FolderConfiguration, content []string) err // and add it to a list of known devices ahead of any checks. func (m *model) OnHello(remoteID protocol.DeviceID, addr net.Addr, hello protocol.Hello) error { if _, ok := m.cfg.Device(remoteID); !ok { - if err := m.db.AddOrUpdatePendingDevice(remoteID, hello.DeviceName, addr.String()); err != nil { + if err := m.observed.AddOrUpdatePendingDevice(remoteID, hello.DeviceName, addr.String()); err != nil { l.Warnf("Failed to persist pending device entry to database: %v", err) } m.evLogger.Log(events.PendingDevicesChanged, map[string][]interface{}{ @@ -2474,14 +2452,14 @@ func (m *model) deviceDidCloseRLocked(deviceID protocol.DeviceID, duration time. } } -func (m *model) RequestGlobal(ctx context.Context, deviceID protocol.DeviceID, folder, name string, blockNo int, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error) { +func (m *model) RequestGlobal(ctx context.Context, deviceID protocol.DeviceID, folder, name string, blockNo int, offset int64, size int, hash []byte, fromTemporary bool) ([]byte, error) { conn, connOK := m.requestConnectionForDevice(deviceID) if !connOK { return nil, fmt.Errorf("requestGlobal: no connection to device: %s", deviceID.Short()) } - l.Debugf("%v REQ(out): %s (%s): %q / %q b=%d o=%d s=%d h=%x wh=%x ft=%t", m, deviceID.Short(), conn, folder, name, blockNo, offset, size, hash, weakHash, fromTemporary) - return conn.Request(ctx, &protocol.Request{Folder: folder, Name: name, BlockNo: blockNo, Offset: offset, Size: size, Hash: hash, WeakHash: weakHash, FromTemporary: fromTemporary}) + l.Debugf("%v REQ(out): %s (%s): %q / %q b=%d o=%d s=%d h=%x ft=%t", m, deviceID.Short(), conn, folder, name, blockNo, offset, size, hash, fromTemporary) + return conn.Request(ctx, &protocol.Request{Folder: folder, Name: name, BlockNo: blockNo, Offset: offset, Size: size, Hash: hash, FromTemporary: fromTemporary}) } // requestConnectionForDevice returns a connection to the given device, to @@ -2525,7 +2503,6 @@ func (m *model) ScanFolders() map[string]error { wg := sync.NewWaitGroup() wg.Add(len(folders)) for _, folder := range folders { - folder := folder go func() { err := m.ScanFolder(folder) if err != nil { @@ -2629,13 +2606,11 @@ func (m *model) generateClusterConfigRLocked(device protocol.DeviceID) (*protoco DisableTempIndexes: folderCfg.DisableTempIndexes, } - fs := m.folderFiles[folderCfg.ID] - // Even if we aren't paused, if we haven't started the folder yet // pretend we are. Otherwise the remote might get confused about // the missing index info (and drop all the info). We will send // another cluster config once the folder is started. - protocolFolder.Paused = folderCfg.Paused || fs == nil + protocolFolder.Paused = folderCfg.Paused for _, folderDevice := range folderCfg.Devices { deviceCfg, _ := m.cfg.Device(folderDevice.DeviceID) @@ -2658,14 +2633,12 @@ func (m *model) generateClusterConfigRLocked(device protocol.DeviceID) (*protoco } } - if fs != nil { - if deviceCfg.DeviceID == m.id { - protocolDevice.IndexID = fs.IndexID(protocol.LocalDeviceID) - protocolDevice.MaxSequence = fs.Sequence(protocol.LocalDeviceID) - } else { - protocolDevice.IndexID = fs.IndexID(deviceCfg.DeviceID) - protocolDevice.MaxSequence = fs.Sequence(deviceCfg.DeviceID) - } + if deviceCfg.DeviceID == m.id { + protocolDevice.IndexID, _ = m.sdb.GetIndexID(folderCfg.ID, protocol.LocalDeviceID) + protocolDevice.MaxSequence, _ = m.sdb.GetDeviceSequence(folderCfg.ID, protocol.LocalDeviceID) + } else { + protocolDevice.IndexID, _ = m.sdb.GetIndexID(folderCfg.ID, deviceCfg.DeviceID) + protocolDevice.MaxSequence, _ = m.sdb.GetDeviceSequence(folderCfg.ID, deviceCfg.DeviceID) } protocolFolder.Devices = append(protocolFolder.Devices, protocolDevice) @@ -2708,7 +2681,7 @@ func (m *model) WatchError(folder string) error { runner, _ := m.folderRunners.Get(folder) m.mut.RUnlock() if err != nil { - return nil // If the folder isn't running, there's no error to report. + return nil //nolint:nilerr // If the folder isn't running, there's no error to report. } return runner.WatchError() } @@ -2762,7 +2735,7 @@ func findByName(slice []*TreeEntry, name string) *TreeEntry { func (m *model) GlobalDirectoryTree(folder, prefix string, levels int, dirsOnly bool) ([]*TreeEntry, error) { m.mut.RLock() - files, ok := m.folderFiles[folder] + _, ok := m.folderCfgs[folder] m.mut.RUnlock() if !ok { return nil, ErrFolderMissing @@ -2775,18 +2748,17 @@ func (m *model) GlobalDirectoryTree(folder, prefix string, levels int, dirsOnly prefix = osutil.NativeFilename(prefix) if prefix != "" && !strings.HasSuffix(prefix, sep) { - prefix = prefix + sep + prefix += sep } - snap, err := files.Snapshot() - if err != nil { - return nil, err - } - defer snap.Release() - snap.WithPrefixedGlobalTruncated(prefix, func(f protocol.FileInfo) bool { + for f, err := range itererr.Zip(m.sdb.AllGlobalFilesPrefix(folder, prefix)) { + if err != nil { + return nil, err + } + // Don't include the prefix itself. - if f.IsInvalid() || f.IsDeleted() || strings.HasPrefix(prefix, f.Name) { - return true + if f.Invalid || f.Deleted || strings.HasPrefix(prefix, f.Name) { + continue } f.Name = strings.Replace(f.Name, prefix, "", 1) @@ -2795,7 +2767,7 @@ func (m *model) GlobalDirectoryTree(folder, prefix string, levels int, dirsOnly base := filepath.Base(f.Name) if levels > -1 && strings.Count(f.Name, sep) > levels { - return true + continue } parent := root @@ -2803,28 +2775,22 @@ func (m *model) GlobalDirectoryTree(folder, prefix string, levels int, dirsOnly for _, path := range strings.Split(dir, sep) { child := findByName(parent.Children, path) if child == nil { - err = fmt.Errorf("could not find child '%s' for path '%s' in parent '%s'", path, f.Name, parent.Name) - return false + return nil, fmt.Errorf("could not find child '%s' for path '%s' in parent '%s'", path, f.Name, parent.Name) } parent = child } } if dirsOnly && !f.IsDirectory() { - return true + continue } parent.Children = append(parent.Children, &TreeEntry{ Name: base, Type: f.Type.String(), ModTime: f.ModTime(), - Size: f.FileSize(), + Size: f.Size, }) - - return true - }) - if err != nil { - return nil, err } return root.Children, nil @@ -2878,46 +2844,42 @@ func (m *model) Availability(folder string, file protocol.FileInfo, block protoc m.mut.RLock() defer m.mut.RUnlock() - fs, ok := m.folderFiles[folder] - cfg := m.folderCfgs[folder] - + cfg, ok := m.folderCfgs[folder] if !ok { return nil, ErrFolderMissing } - snap, err := fs.Snapshot() - if err != nil { - return nil, err - } - defer snap.Release() - - return m.blockAvailabilityRLocked(cfg, snap, file, block), nil + return m.blockAvailabilityRLocked(cfg, file, block), nil } -func (m *model) blockAvailability(cfg config.FolderConfiguration, snap *db.Snapshot, file protocol.FileInfo, block protocol.BlockInfo) []Availability { +func (m *model) blockAvailability(cfg config.FolderConfiguration, file protocol.FileInfo, block protocol.BlockInfo) []Availability { m.mut.RLock() defer m.mut.RUnlock() - return m.blockAvailabilityRLocked(cfg, snap, file, block) + return m.blockAvailabilityRLocked(cfg, file, block) } -func (m *model) blockAvailabilityRLocked(cfg config.FolderConfiguration, snap *db.Snapshot, file protocol.FileInfo, block protocol.BlockInfo) []Availability { +func (m *model) blockAvailabilityRLocked(cfg config.FolderConfiguration, file protocol.FileInfo, block protocol.BlockInfo) []Availability { var candidates []Availability - candidates = append(candidates, m.fileAvailabilityRLocked(cfg, snap, file)...) + candidates = append(candidates, m.fileAvailabilityRLocked(cfg, file)...) candidates = append(candidates, m.blockAvailabilityFromTemporaryRLocked(cfg, file, block)...) return candidates } -func (m *model) fileAvailability(cfg config.FolderConfiguration, snap *db.Snapshot, file protocol.FileInfo) []Availability { +func (m *model) fileAvailability(cfg config.FolderConfiguration, file protocol.FileInfo) []Availability { m.mut.RLock() defer m.mut.RUnlock() - return m.fileAvailabilityRLocked(cfg, snap, file) + return m.fileAvailabilityRLocked(cfg, file) } -func (m *model) fileAvailabilityRLocked(cfg config.FolderConfiguration, snap *db.Snapshot, file protocol.FileInfo) []Availability { +func (m *model) fileAvailabilityRLocked(cfg config.FolderConfiguration, file protocol.FileInfo) []Availability { var availabilities []Availability - for _, device := range snap.Availability(file.Name) { + devs, err := m.sdb.GetGlobalAvailability(cfg.ID, file.Name) + if err != nil { + return nil + } + for _, device := range devs { if _, ok := m.remoteFolderStates[device]; !ok { continue } @@ -2954,15 +2916,14 @@ func (m *model) BringToFront(folder, file string) { } func (m *model) ResetFolder(folder string) error { - m.mut.RLock() - defer m.mut.RUnlock() + m.mut.Lock() + defer m.mut.Unlock() _, ok := m.folderRunners.Get(folder) if ok { return errors.New("folder must be paused when resetting") } l.Infof("Cleaning metadata for reset folder %q", folder) - db.DropFolder(m.db, folder) - return nil + return m.sdb.DropFolder(folder) } func (m *model) String() string { @@ -3076,7 +3037,7 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool { for deviceID, toCfg := range toDevices { fromCfg, ok := fromDevices[deviceID] if !ok { - sr := stats.NewDeviceStatisticsReference(m.db, deviceID) + sr := stats.NewDeviceStatisticsReference(db.NewTyped(m.sdb, "devicestats/"+deviceID.String())) m.mut.Lock() m.deviceStatRefs[deviceID] = sr m.mut.Unlock() @@ -3169,7 +3130,7 @@ func (m *model) setConnRequestLimitersLocked(cfg config.DeviceConfiguration) { func (m *model) cleanPending(existingDevices map[protocol.DeviceID]config.DeviceConfiguration, existingFolders map[string]config.FolderConfiguration, ignoredDevices deviceIDSet, removedFolders map[string]struct{}) { var removedPendingFolders []map[string]string - pendingFolders, err := m.db.PendingFolders() + pendingFolders, err := m.observed.PendingFolders() if err != nil { msg := "Could not iterate through pending folder entries for cleanup" l.Warnf("%v: %v", msg, err) @@ -3182,7 +3143,7 @@ func (m *model) cleanPending(existingDevices map[protocol.DeviceID]config.Device // folders as well, assuming the folder is no longer of interest // at all (but might become pending again). l.Debugf("Discarding pending removed folder %v from all devices", folderID) - if err := m.db.RemovePendingFolder(folderID); err != nil { + if err := m.observed.RemovePendingFolder(folderID); err != nil { msg := "Failed to remove pending folder entry" l.Warnf("%v (%v): %v", msg, folderID, err) m.evLogger.Log(events.Failure, msg) @@ -3209,7 +3170,7 @@ func (m *model) cleanPending(existingDevices map[protocol.DeviceID]config.Device } continue removeFolderForDevice: - if err := m.db.RemovePendingFolderForDevice(folderID, deviceID); err != nil { + if err := m.observed.RemovePendingFolderForDevice(folderID, deviceID); err != nil { msg := "Failed to remove pending folder-device entry" l.Warnf("%v (%v, %v): %v", msg, folderID, deviceID, err) m.evLogger.Log(events.Failure, msg) @@ -3228,7 +3189,7 @@ func (m *model) cleanPending(existingDevices map[protocol.DeviceID]config.Device } var removedPendingDevices []map[string]string - pendingDevices, err := m.db.PendingDevices() + pendingDevices, err := m.observed.PendingDevices() if err != nil { msg := "Could not iterate through pending device entries for cleanup" l.Warnf("%v: %v", msg, err) @@ -3246,7 +3207,7 @@ func (m *model) cleanPending(existingDevices map[protocol.DeviceID]config.Device } continue removeDevice: - if err := m.db.RemovePendingDevice(deviceID); err != nil { + if err := m.observed.RemovePendingDevice(deviceID); err != nil { msg := "Failed to remove pending device entry" l.Warnf("%v: %v", msg, err) m.evLogger.Log(events.Failure, msg) @@ -3283,20 +3244,20 @@ func (m *model) checkFolderRunningRLocked(folder string) error { // PendingDevices lists unknown devices that tried to connect. func (m *model) PendingDevices() (map[protocol.DeviceID]db.ObservedDevice, error) { - return m.db.PendingDevices() + return m.observed.PendingDevices() } // PendingFolders lists folders that we don't yet share with the offering devices. It // returns the entries grouped by folder and filters for a given device unless the // argument is specified as EmptyDeviceID. func (m *model) PendingFolders(device protocol.DeviceID) (map[string]db.PendingFolder, error) { - return m.db.PendingFoldersForDevice(device) + return m.observed.PendingFoldersForDevice(device) } // DismissPendingDevices removes the record of a specific pending device. func (m *model) DismissPendingDevice(device protocol.DeviceID) error { l.Debugf("Discarding pending device %v", device) - err := m.db.RemovePendingDevice(device) + err := m.observed.RemovePendingDevice(device) if err != nil { return err } @@ -3316,7 +3277,7 @@ func (m *model) DismissPendingFolder(device protocol.DeviceID, folder string) er var removedPendingFolders []map[string]string if device == protocol.EmptyDeviceID { l.Debugf("Discarding pending removed folder %s from all devices", folder) - err := m.db.RemovePendingFolder(folder) + err := m.observed.RemovePendingFolder(folder) if err != nil { return err } @@ -3325,7 +3286,7 @@ func (m *model) DismissPendingFolder(device protocol.DeviceID, folder string) er } } else { l.Debugf("Discarding pending folder %s from device %v", folder, device) - err := m.db.RemovePendingFolderForDevice(folder, device) + err := m.observed.RemovePendingFolderForDevice(folder, device) if err != nil { return err } @@ -3454,7 +3415,7 @@ type storedEncryptionToken struct { } func readEncryptionToken(cfg config.FolderConfiguration) ([]byte, error) { - fd, err := cfg.Filesystem(nil).Open(encryptionTokenPath(cfg)) + fd, err := cfg.Filesystem().Open(encryptionTokenPath(cfg)) if err != nil { return nil, err } @@ -3468,7 +3429,7 @@ func readEncryptionToken(cfg config.FolderConfiguration) ([]byte, error) { func writeEncryptionToken(token []byte, cfg config.FolderConfiguration) error { tokenName := encryptionTokenPath(cfg) - fd, err := cfg.Filesystem(nil).OpenFile(tokenName, fs.OptReadWrite|fs.OptCreate, 0o666) + fd, err := cfg.Filesystem().OpenFile(tokenName, fs.OptReadWrite|fs.OptCreate, 0o666) if err != nil { return err } @@ -3499,8 +3460,8 @@ type updatedPendingFolder struct { // redactPathError checks if the error is actually a os.PathError, and if yes // returns a redactedError with the path removed. func redactPathError(err error) (error, bool) { - perr, ok := err.(*os.PathError) - if !ok { + var perr *os.PathError + if !errors.As(err, &perr) { return nil, false } return &redactedError{ diff --git a/lib/model/model_test.go b/lib/model/model_test.go index 3f131998f..34a76a7ee 100644 --- a/lib/model/model_test.go +++ b/lib/model/model_test.go @@ -13,6 +13,7 @@ import ( "errors" "fmt" "io" + "iter" mrand "math/rand" "os" "path/filepath" @@ -24,10 +25,10 @@ import ( "testing" "time" + "github.com/syncthing/syncthing/internal/db" + "github.com/syncthing/syncthing/internal/itererr" "github.com/syncthing/syncthing/lib/build" "github.com/syncthing/syncthing/lib/config" - "github.com/syncthing/syncthing/lib/db" - "github.com/syncthing/syncthing/lib/db/backend" "github.com/syncthing/syncthing/lib/events" "github.com/syncthing/syncthing/lib/fs" "github.com/syncthing/syncthing/lib/ignore" @@ -77,7 +78,7 @@ func addFolderDevicesToClusterConfig(cc *protocol.ClusterConfig, remote protocol func TestRequest(t *testing.T) { wrapper, fcfg, cancel := newDefaultCfgWrapper() - ffs := fcfg.Filesystem(nil) + ffs := fcfg.Filesystem() defer cancel() m := setupModel(t, wrapper) defer cleanupModel(m) @@ -165,7 +166,7 @@ func BenchmarkIndex_100(b *testing.B) { func benchmarkIndex(b *testing.B, nfiles int) { m, _, fcfg, wcfgCancel := setupModelWithConnection(b) defer wcfgCancel() - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) files := genFiles(nfiles) must(b, m.Index(device1Conn, &protocol.Index{Folder: fcfg.ID, Files: files})) @@ -192,7 +193,7 @@ func BenchmarkIndexUpdate_10000_1(b *testing.B) { func benchmarkIndexUpdate(b *testing.B, nfiles, nufiles int) { m, _, fcfg, wcfgCancel := setupModelWithConnection(b) defer wcfgCancel() - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) files := genFiles(nfiles) ufiles := genFiles(nufiles) @@ -222,7 +223,7 @@ func BenchmarkRequestOut(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - data, err := m.RequestGlobal(context.Background(), device1, "default", files[i%n].Name, 0, 0, 32, nil, 0, false) + data, err := m.RequestGlobal(context.Background(), device1, "default", files[i%n].Name, 0, 0, 32, nil, false) if err != nil { b.Error(err) } @@ -235,7 +236,7 @@ func BenchmarkRequestOut(b *testing.B) { func BenchmarkRequestInSingleFile(b *testing.B) { w, cancel := newConfigWrapper(defaultCfg) defer cancel() - ffs := w.FolderList()[0].Filesystem(nil) + ffs := w.FolderList()[0].Filesystem() m := setupModel(b, w) defer cleanupModel(m) @@ -1195,7 +1196,7 @@ func TestAutoAcceptPrefersLabel(t *testing.T) { func TestAutoAcceptFallsBackToID(t *testing.T) { // Prefers label, falls back to ID. m, cancel := newState(t, defaultAutoAcceptCfg) - ffs := defaultFolderConfig.Filesystem(nil) + ffs := defaultFolderConfig.Filesystem() id := srand.String(8) label := srand.String(8) if err := ffs.MkdirAll(label, 0o777); err != nil { @@ -1484,7 +1485,7 @@ func changeIgnores(t *testing.T, m *testModel, expected []string) { func TestIgnores(t *testing.T) { w, cancel := newConfigWrapper(defaultCfg) defer cancel() - ffs := w.FolderList()[0].Filesystem(nil) + ffs := w.FolderList()[0].Filesystem() m := setupModel(t, w) defer cleanupModel(m) @@ -1519,7 +1520,7 @@ func TestIgnores(t *testing.T) { ID: "fresh", Path: "XXX", FilesystemType: config.FilesystemTypeFake, } - ignores := ignore.New(fcfg.Filesystem(nil), ignore.WithCache(m.cfg.Options().CacheIgnoredFiles)) + ignores := ignore.New(fcfg.Filesystem(), ignore.WithCache(m.cfg.Options().CacheIgnoredFiles)) m.mut.Lock() m.folderCfgs[fcfg.ID] = fcfg m.folderIgnores[fcfg.ID] = ignores @@ -1551,7 +1552,7 @@ func TestIgnores(t *testing.T) { func TestEmptyIgnores(t *testing.T) { w, cancel := newConfigWrapper(defaultCfg) defer cancel() - ffs := w.FolderList()[0].Filesystem(nil) + ffs := w.FolderList()[0].Filesystem() m := setupModel(t, w) defer cleanupModel(m) @@ -1624,12 +1625,11 @@ func TestROScanRecovery(t *testing.T) { defer cancel() m := newModel(t, cfg, myID, nil) - set := newFileSet(t, "default", m.db) - set.Update(protocol.LocalDeviceID, []protocol.FileInfo{ + m.sdb.Update("default", protocol.LocalDeviceID, []protocol.FileInfo{ {Name: "dummyfile", Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 1}}}}, }) - ffs := fcfg.Filesystem(nil) + ffs := fcfg.Filesystem() // Remove marker to generate an error ffs.Remove(fcfg.MarkerName) @@ -1671,12 +1671,11 @@ func TestRWScanRecovery(t *testing.T) { defer cancel() m := newModel(t, cfg, myID, nil) - set := newFileSet(t, "default", m.db) - set.Update(protocol.LocalDeviceID, []protocol.FileInfo{ + m.sdb.Update("default", protocol.LocalDeviceID, []protocol.FileInfo{ {Name: "dummyfile", Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 1}}}}, }) - ffs := fcfg.Filesystem(nil) + ffs := fcfg.Filesystem() // Generate error if err := ffs.Remove(config.DefaultMarkerName); err != nil { @@ -1702,8 +1701,9 @@ func TestRWScanRecovery(t *testing.T) { func TestGlobalDirectoryTree(t *testing.T) { m, conn, fcfg, wCancel := setupModelWithConnection(t) defer wCancel() - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) + var seq int64 b := func(isfile bool, path ...string) protocol.FileInfo { typ := protocol.FileInfoTypeDirectory var blocks []protocol.BlockInfo @@ -1712,12 +1712,14 @@ func TestGlobalDirectoryTree(t *testing.T) { typ = protocol.FileInfoTypeFile blocks = []protocol.BlockInfo{{Offset: 0x0, Size: 0xa, Hash: []uint8{0x2f, 0x72, 0xcc, 0x11, 0xa6, 0xfc, 0xd0, 0x27, 0x1e, 0xce, 0xf8, 0xc6, 0x10, 0x56, 0xee, 0x1e, 0xb1, 0x24, 0x3b, 0xe3, 0x80, 0x5b, 0xf9, 0xa9, 0xdf, 0x98, 0xf9, 0x2f, 0x76, 0x36, 0xb0, 0x5c}}} } + seq++ return protocol.FileInfo{ Name: filepath.Join(path...), Type: typ, ModifiedS: 0x666, Blocks: blocks, Size: 0xa, + Sequence: seq, } } f := func(name string) *TreeEntry { @@ -1809,13 +1811,13 @@ func TestGlobalDirectoryTree(t *testing.T) { result, _ := m.GlobalDirectoryTree("default", "", -1, false) if mm(result) != mm(expectedResult) { - t.Errorf("Does not match:\n%s\n============\n%s", mm(result), mm(expectedResult)) + t.Fatalf("Does not match:\n%s\n============\n%s", mm(result), mm(expectedResult)) } result, _ = m.GlobalDirectoryTree("default", "another", -1, false) if mm(result) != mm(findByName(expectedResult, "another").Children) { - t.Errorf("Does not match:\n%s\n============\n%s", mm(result), mm(findByName(expectedResult, "another").Children)) + t.Fatalf("Does not match:\n%s\n============\n%s", mm(result), mm(findByName(expectedResult, "another").Children)) } result, _ = m.GlobalDirectoryTree("default", "", 0, false) @@ -1827,7 +1829,7 @@ func TestGlobalDirectoryTree(t *testing.T) { } if mm(result) != mm(currentResult) { - t.Errorf("Does not match:\n%s\n============\n%s", mm(result), mm(currentResult)) + t.Fatalf("Does not match:\n%s\n============\n%s", mm(result), mm(currentResult)) } result, _ = m.GlobalDirectoryTree("default", "", 1, false) @@ -1848,7 +1850,7 @@ func TestGlobalDirectoryTree(t *testing.T) { } if mm(result) != mm(currentResult) { - t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) + t.Fatalf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) } result, _ = m.GlobalDirectoryTree("default", "", -1, true) @@ -1878,7 +1880,7 @@ func TestGlobalDirectoryTree(t *testing.T) { } if mm(result) != mm(currentResult) { - t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) + t.Fatalf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) } result, _ = m.GlobalDirectoryTree("default", "", 1, true) @@ -1897,7 +1899,7 @@ func TestGlobalDirectoryTree(t *testing.T) { } if mm(result) != mm(currentResult) { - t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) + t.Fatalf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) } result, _ = m.GlobalDirectoryTree("default", "another", 0, false) @@ -1907,7 +1909,7 @@ func TestGlobalDirectoryTree(t *testing.T) { } if mm(result) != mm(currentResult) { - t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) + t.Fatalf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) } result, _ = m.GlobalDirectoryTree("default", "some/directory", 0, false) @@ -1916,7 +1918,7 @@ func TestGlobalDirectoryTree(t *testing.T) { } if mm(result) != mm(currentResult) { - t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) + t.Fatalf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) } result, _ = m.GlobalDirectoryTree("default", "some/directory", 1, false) @@ -1927,7 +1929,7 @@ func TestGlobalDirectoryTree(t *testing.T) { } if mm(result) != mm(currentResult) { - t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) + t.Fatalf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) } result, _ = m.GlobalDirectoryTree("default", "some/directory", 2, false) @@ -1940,7 +1942,7 @@ func TestGlobalDirectoryTree(t *testing.T) { } if mm(result) != mm(currentResult) { - t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) + t.Fatalf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) } result, _ = m.GlobalDirectoryTree("default", "another", -1, true) @@ -1953,7 +1955,7 @@ func TestGlobalDirectoryTree(t *testing.T) { } if mm(result) != mm(currentResult) { - t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) + t.Fatalf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) } // No prefix matching! @@ -1961,7 +1963,7 @@ func TestGlobalDirectoryTree(t *testing.T) { currentResult = []*TreeEntry{} if mm(result) != mm(currentResult) { - t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) + t.Fatalf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) } } @@ -2006,7 +2008,7 @@ func BenchmarkTree_100_10(b *testing.B) { func benchmarkTree(b *testing.B, n1, n2 int) { m, _, fcfg, wcfgCancel := setupModelWithConnection(b) defer wcfgCancel() - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) m.ScanFolder(fcfg.ID) files := genDeepFiles(n1, n2) @@ -2023,7 +2025,7 @@ func benchmarkTree(b *testing.B, n1, n2 int) { func TestIssue3028(t *testing.T) { w, cancel := newConfigWrapper(defaultCfg) defer cancel() - ffs := w.FolderList()[0].Filesystem(nil) + ffs := w.FolderList()[0].Filesystem() m := setupModel(t, w) defer cleanupModel(m) @@ -2035,8 +2037,8 @@ func TestIssue3028(t *testing.T) { // Scan, and get a count of how many files are there now m.ScanFolderSubdirs("default", []string{"testrm", "testrm2"}) - locorigfiles := localSize(t, m, "default").Files - globorigfiles := globalSize(t, m, "default").Files + locorigfiles := mustV(m.LocalSize("default", protocol.LocalDeviceID)).Files + globorigfiles := mustV(m.GlobalSize("default")).Files // Delete @@ -2047,8 +2049,8 @@ func TestIssue3028(t *testing.T) { // deleted files increases by two m.ScanFolderSubdirs("default", []string{"testrm", "testrm2"}) - loc := localSize(t, m, "default") - glob := globalSize(t, m, "default") + loc := mustV(m.LocalSize("default", protocol.LocalDeviceID)) + glob := mustV(m.GlobalSize("default")) if loc.Files != locorigfiles-2 { t.Errorf("Incorrect local accounting; got %d current files, expected %d", loc.Files, locorigfiles-2) @@ -2123,24 +2125,22 @@ func TestIssue4357(t *testing.T) { func TestIndexesForUnknownDevicesDropped(t *testing.T) { m := newModel(t, defaultCfgWrapper, myID, nil) - files := newFileSet(t, "default", m.db) - files.Drop(device1) - files.Update(device1, genFiles(1)) - files.Drop(device2) - files.Update(device2, genFiles(1)) + m.sdb.DropAllFiles("default", device1) + m.sdb.Update("default", device1, genFiles(1)) + m.sdb.DropAllFiles("default", device2) + m.sdb.Update("default", device2, genFiles(1)) - if len(files.ListDevices()) != 2 { + if devs, err := m.sdb.ListDevicesForFolder("default"); err != nil || len(devs) != 2 { + t.Log(devs, err) t.Error("expected two devices") } m.newFolder(defaultFolderConfig, false) defer cleanupModel(m) - // Remote sequence is cached, hence need to recreated. - files = newFileSet(t, "default", m.db) - - if l := len(files.ListDevices()); l != 1 { - t.Errorf("Expected one device got %v", l) + if devs, err := m.sdb.ListDevicesForFolder("default"); err != nil || len(devs) != 1 { + t.Log(devs, err) + t.Error("expected one device") } } @@ -2266,7 +2266,7 @@ func TestIssue3829(t *testing.T) { func TestIssue4573(t *testing.T) { w, fcfg, wCancel := newDefaultCfgWrapper() defer wCancel() - testFs := fcfg.Filesystem(nil) + testFs := fcfg.Filesystem() defer os.RemoveAll(testFs.URI()) must(t, testFs.MkdirAll("inaccessible", 0o755)) @@ -2296,7 +2296,7 @@ func TestIssue4573(t *testing.T) { func TestInternalScan(t *testing.T) { w, fcfg, wCancel := newDefaultCfgWrapper() defer wCancel() - testFs := fcfg.Filesystem(nil) + testFs := fcfg.Filesystem() defer os.RemoveAll(testFs.URI()) testCases := map[string]func(protocol.FileInfo) bool{ @@ -2368,12 +2368,11 @@ func TestCustomMarkerName(t *testing.T) { }) defer cancel() - ffs := fcfg.Filesystem(nil) + ffs := fcfg.Filesystem() m := newModel(t, cfg, myID, nil) - set := newFileSet(t, "default", m.db) - set.Update(protocol.LocalDeviceID, []protocol.FileInfo{ + m.sdb.Update("default", protocol.LocalDeviceID, []protocol.FileInfo{ {Name: "dummyfile"}, }) @@ -2397,7 +2396,7 @@ func TestCustomMarkerName(t *testing.T) { func TestRemoveDirWithContent(t *testing.T) { m, conn, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - tfs := fcfg.Filesystem(nil) + tfs := fcfg.Filesystem() defer cleanupModelAndRemoveDir(m, tfs.URI()) tfs.MkdirAll("dirwith", 0o755) @@ -2459,7 +2458,7 @@ func TestIssue4475(t *testing.T) { m, conn, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() defer cleanupModel(m) - testFs := fcfg.Filesystem(nil) + testFs := fcfg.Filesystem() // Scenario: Dir is deleted locally and before syncing/index exchange // happens, a file is create in that dir on the remote. @@ -2521,7 +2520,7 @@ func TestVersionRestore(t *testing.T) { fcfg := newFolderConfiguration(defaultCfgWrapper, "default", "default", config.FilesystemTypeFake, srand.String(32)) fcfg.Versioning.Type = "simple" fcfg.FSWatcherEnabled = false - filesystem := fcfg.Filesystem(nil) + filesystem := fcfg.Filesystem() rawConfig := config.Configuration{ Version: config.CurrentVersion, @@ -2755,7 +2754,7 @@ func TestIssue4094(t *testing.T) { t.Fatalf("failed setting ignores: %v", err) } - if _, err := fcfg.Filesystem(nil).Lstat(".stignore"); err != nil { + if _, err := fcfg.Filesystem().Lstat(".stignore"); err != nil { t.Fatalf("failed stating .stignore: %v", err) } } @@ -2785,7 +2784,7 @@ func TestIssue4903(t *testing.T) { t.Fatalf("expected path missing error, got: %v, debug: %s", err, fcfg.CheckPath()) } - if _, err := fcfg.Filesystem(nil).Lstat("."); !fs.IsNotExist(err) { + if _, err := fcfg.Filesystem().Lstat("."); !fs.IsNotExist(err) { t.Fatalf("Expected missing path error, got: %v", err) } } @@ -2795,7 +2794,7 @@ func TestIssue5002(t *testing.T) { w, fcfg, wCancel := newDefaultCfgWrapper() defer wCancel() - ffs := fcfg.Filesystem(nil) + ffs := fcfg.Filesystem() fd, err := ffs.Create("foo") must(t, err) @@ -2816,15 +2815,15 @@ func TestIssue5002(t *testing.T) { } blockSize := int32(file.BlockSize()) - m.recheckFile(protocol.LocalDeviceID, "default", "foo", file.Size-int64(blockSize), []byte{1, 2, 3, 4}, 0) - m.recheckFile(protocol.LocalDeviceID, "default", "foo", file.Size, []byte{1, 2, 3, 4}, 0) // panic - m.recheckFile(protocol.LocalDeviceID, "default", "foo", file.Size+int64(blockSize), []byte{1, 2, 3, 4}, 0) + m.recheckFile(protocol.LocalDeviceID, "default", "foo", file.Size-int64(blockSize), []byte{1, 2, 3, 4}) + m.recheckFile(protocol.LocalDeviceID, "default", "foo", file.Size, []byte{1, 2, 3, 4}) // panic + m.recheckFile(protocol.LocalDeviceID, "default", "foo", file.Size+int64(blockSize), []byte{1, 2, 3, 4}) } func TestParentOfUnignored(t *testing.T) { w, fcfg, wCancel := newDefaultCfgWrapper() defer wCancel() - ffs := fcfg.Filesystem(nil) + ffs := fcfg.Filesystem() must(t, ffs.Mkdir("bar", 0o755)) must(t, ffs.Mkdir("baz", 0o755)) @@ -2903,7 +2902,7 @@ func TestFolderRestartZombies(t *testing.T) { func TestRequestLimit(t *testing.T) { wrapper, fcfg, cancel := newDefaultCfgWrapper() - ffs := fcfg.Filesystem(nil) + ffs := fcfg.Filesystem() file := "tmpfile" fd, err := ffs.Create(file) @@ -2963,7 +2962,7 @@ func TestConnCloseOnRestart(t *testing.T) { w, fcfg, wCancel := newDefaultCfgWrapper() defer wCancel() m := setupModel(t, w) - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) br := &testutil.BlockingRW{} nw := &testutil.NoopRW{} @@ -3006,7 +3005,7 @@ func TestModTimeWindow(t *testing.T) { defer wCancel() tfs := modtimeTruncatingFS{ trunc: 0, - Filesystem: fcfg.Filesystem(nil), + Filesystem: fcfg.Filesystem(), } // fcfg.RawModTimeWindowS = 2 setFolder(t, w, fcfg) @@ -3066,7 +3065,7 @@ func TestModTimeWindow(t *testing.T) { func TestDevicePause(t *testing.T) { m, _, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) sub := m.evLogger.Subscribe(events.DevicePaused) defer sub.Unsubscribe() @@ -3096,7 +3095,7 @@ func TestDevicePause(t *testing.T) { func TestDeviceWasSeen(t *testing.T) { m, _, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) m.deviceWasSeen(device1) @@ -3191,7 +3190,7 @@ func TestRenameSequenceOrder(t *testing.T) { numFiles := 20 - ffs := fcfg.Filesystem(nil) + ffs := fcfg.Filesystem() for i := 0; i < numFiles; i++ { v := fmt.Sprintf("%d", i) writeFile(t, ffs, v, []byte(v)) @@ -3199,14 +3198,7 @@ func TestRenameSequenceOrder(t *testing.T) { m.ScanFolders() - count := 0 - snap := dbSnapshot(t, m, "default") - snap.WithHave(protocol.LocalDeviceID, func(i protocol.FileInfo) bool { - count++ - return true - }) - snap.Release() - + count := countIterator[protocol.FileInfo](t)(m.LocalFiles("default", protocol.LocalDeviceID)) if count != numFiles { t.Errorf("Unexpected count: %d != %d", count, numFiles) } @@ -3226,14 +3218,11 @@ func TestRenameSequenceOrder(t *testing.T) { // Scan m.ScanFolders() - // Verify sequence of a appearing is followed by c disappearing. - snap = dbSnapshot(t, m, "default") - defer snap.Release() - var firstExpectedSequence int64 var secondExpectedSequence int64 failed := false - snap.WithHaveSequence(0, func(i protocol.FileInfo) bool { + it, errFn := m.LocalFilesSequenced("default", protocol.LocalDeviceID, 0) + for i := range it { t.Log(i) if i.FileName() == "17" { firstExpectedSequence = i.SequenceNo() + 1 @@ -3247,8 +3236,10 @@ func TestRenameSequenceOrder(t *testing.T) { if i.FileName() == "16" { failed = i.SequenceNo() != secondExpectedSequence || failed } - return true - }) + } + if err := errFn(); err != nil { + t.Fatal(err) + } if failed { t.Fail() } @@ -3260,19 +3251,12 @@ func TestRenameSameFile(t *testing.T) { m := setupModel(t, wcfg) defer cleanupModel(m) - ffs := fcfg.Filesystem(nil) + ffs := fcfg.Filesystem() writeFile(t, ffs, "file", []byte("file")) m.ScanFolders() - count := 0 - snap := dbSnapshot(t, m, "default") - snap.WithHave(protocol.LocalDeviceID, func(i protocol.FileInfo) bool { - count++ - return true - }) - snap.Release() - + count := countIterator[protocol.FileInfo](t)(m.LocalFiles("default", protocol.LocalDeviceID)) if count != 1 { t.Errorf("Unexpected count: %d != %d", count, 1) } @@ -3285,12 +3269,10 @@ func TestRenameSameFile(t *testing.T) { m.ScanFolders() - snap = dbSnapshot(t, m, "default") - defer snap.Release() - prevSeq := int64(0) seen := false - snap.WithHaveSequence(0, func(i protocol.FileInfo) bool { + it, errFn := m.LocalFilesSequenced("default", protocol.LocalDeviceID, 0) + for i := range it { if i.SequenceNo() <= prevSeq { t.Fatalf("non-increasing sequences: %d <= %d", i.SequenceNo(), prevSeq) } @@ -3301,84 +3283,9 @@ func TestRenameSameFile(t *testing.T) { seen = true } prevSeq = i.SequenceNo() - return true - }) -} - -func TestRenameEmptyFile(t *testing.T) { - wcfg, fcfg, wcfgCancel := newDefaultCfgWrapper() - defer wcfgCancel() - m := setupModel(t, wcfg) - defer cleanupModel(m) - - ffs := fcfg.Filesystem(nil) - - writeFile(t, ffs, "file", []byte("data")) - writeFile(t, ffs, "empty", nil) - - m.ScanFolders() - - snap := dbSnapshot(t, m, "default") - defer snap.Release() - empty, eok := snap.Get(protocol.LocalDeviceID, "empty") - if !eok { - t.Fatal("failed to find empty file") } - file, fok := snap.Get(protocol.LocalDeviceID, "file") - if !fok { - t.Fatal("failed to find non-empty file") - } - - count := 0 - snap.WithBlocksHash(empty.BlocksHash, func(_ protocol.FileInfo) bool { - count++ - return true - }) - - if count != 0 { - t.Fatalf("Found %d entries for empty file, expected 0", count) - } - - count = 0 - snap.WithBlocksHash(file.BlocksHash, func(_ protocol.FileInfo) bool { - count++ - return true - }) - - if count != 1 { - t.Fatalf("Found %d entries for non-empty file, expected 1", count) - } - - must(t, ffs.Rename("file", "new-file")) - must(t, ffs.Rename("empty", "new-empty")) - - // Scan - m.ScanFolders() - - snap = dbSnapshot(t, m, "default") - defer snap.Release() - - count = 0 - snap.WithBlocksHash(empty.BlocksHash, func(_ protocol.FileInfo) bool { - count++ - return true - }) - - if count != 0 { - t.Fatalf("Found %d entries for empty file, expected 0", count) - } - - count = 0 - snap.WithBlocksHash(file.BlocksHash, func(i protocol.FileInfo) bool { - count++ - if i.FileName() != "new-file" { - t.Fatalf("unexpected file name %s, expected new-file", i.FileName()) - } - return true - }) - - if count != 1 { - t.Fatalf("Found %d entries for non-empty file, expected 1", count) + if err := errFn(); err != nil { + t.Fatal(err) } } @@ -3388,7 +3295,7 @@ func TestBlockListMap(t *testing.T) { m := setupModel(t, wcfg) defer cleanupModel(m) - ffs := fcfg.Filesystem(nil) + ffs := fcfg.Filesystem() writeFile(t, ffs, "one", []byte("content")) writeFile(t, ffs, "two", []byte("content")) writeFile(t, ffs, "three", []byte("content")) @@ -3397,23 +3304,25 @@ func TestBlockListMap(t *testing.T) { m.ScanFolders() - snap := dbSnapshot(t, m, "default") - defer snap.Release() - fi, ok := snap.Get(protocol.LocalDeviceID, "one") + fi, ok, err := m.model.CurrentFolderFile("default", "one") + if err != nil { + t.Fatal(err) + } if !ok { t.Error("failed to find existing file") } var paths []string - snap.WithBlocksHash(fi.BlocksHash, func(fi protocol.FileInfo) bool { - paths = append(paths, fi.FileName()) - return true - }) - snap.Release() + for fi, err := range itererr.Zip(m.model.AllForBlocksHash(fcfg.ID, fi.BlocksHash)) { + if err != nil { + t.Fatal(err) + } + paths = append(paths, fi.Name) + } expected := []string{"one", "two", "three", "four", "five"} if !equalStringsInAnyOrder(paths, expected) { - t.Errorf("expected %q got %q", expected, paths) + t.Fatalf("expected %q got %q", expected, paths) } // Fudge the files around @@ -3434,19 +3343,18 @@ func TestBlockListMap(t *testing.T) { m.ScanFolders() // Check we're left with 2 of the 5 - snap = dbSnapshot(t, m, "default") - defer snap.Release() paths = paths[:0] - snap.WithBlocksHash(fi.BlocksHash, func(fi protocol.FileInfo) bool { - paths = append(paths, fi.FileName()) - return true - }) - snap.Release() + for fi, err := range itererr.Zip(m.model.AllForBlocksHash(fcfg.ID, fi.BlocksHash)) { + if err != nil { + t.Fatal(err) + } + paths = append(paths, fi.Name) + } expected = []string{"new-three", "five"} if !equalStringsInAnyOrder(paths, expected) { - t.Errorf("expected %q got %q", expected, paths) + t.Fatalf("expected %q got %q", expected, paths) } } @@ -3456,16 +3364,17 @@ func TestScanRenameCaseOnly(t *testing.T) { m := setupModel(t, wcfg) defer cleanupModel(m) - ffs := fcfg.Filesystem(nil) + ffs := fcfg.Filesystem() name := "foo" writeFile(t, ffs, name, []byte("contents")) m.ScanFolders() - snap := dbSnapshot(t, m, fcfg.ID) - defer snap.Release() found := false - snap.WithHave(protocol.LocalDeviceID, func(i protocol.FileInfo) bool { + for i, err := range itererr.Zip(m.LocalFiles(fcfg.ID, protocol.LocalDeviceID)) { + if err != nil { + t.Fatal(err) + } if found { t.Fatal("got more than one file") } @@ -3473,21 +3382,20 @@ func TestScanRenameCaseOnly(t *testing.T) { t.Fatalf("got file %v, expected %v", i.FileName(), name) } found = true - return true - }) - snap.Release() + } upper := strings.ToUpper(name) must(t, ffs.Rename(name, upper)) m.ScanFolders() - snap = dbSnapshot(t, m, fcfg.ID) - defer snap.Release() found = false - snap.WithHave(protocol.LocalDeviceID, func(i protocol.FileInfo) bool { + for i, err := range itererr.Zip(m.LocalFiles(fcfg.ID, protocol.LocalDeviceID)) { + if err != nil { + t.Fatal(err) + } if i.FileName() == name { if i.IsDeleted() { - return true + continue } t.Fatal("renamed file not deleted") } @@ -3498,8 +3406,7 @@ func TestScanRenameCaseOnly(t *testing.T) { t.Fatal("got more than the expected files") } found = true - return true - }) + } } func TestClusterConfigOnFolderAdd(t *testing.T) { @@ -3574,7 +3481,7 @@ func TestAddFolderCompletion(t *testing.T) { func TestScanDeletedROChangedOnSR(t *testing.T) { m, conn, fcfg, wCancel := setupModelWithConnection(t) - ffs := fcfg.Filesystem(nil) + ffs := fcfg.Filesystem() defer wCancel() defer cleanupModelAndRemoveDir(m, ffs.URI()) fcfg.Type = config.FolderTypeReceiveOnly @@ -3596,7 +3503,7 @@ func TestScanDeletedROChangedOnSR(t *testing.T) { must(t, ffs.Remove(name)) m.ScanFolders() - if receiveOnlyChangedSize(t, m, fcfg.ID).Deleted != 1 { + if mustV(m.ReceiveOnlySize(fcfg.ID)).Deleted != 1 { t.Fatal("expected one receive only changed deleted item") } @@ -3604,10 +3511,10 @@ func TestScanDeletedROChangedOnSR(t *testing.T) { setFolder(t, m.cfg, fcfg) m.ScanFolders() - if receiveOnlyChangedSize(t, m, fcfg.ID).Deleted != 0 { + if mustV(m.ReceiveOnlySize(fcfg.ID)).Deleted != 0 { t.Fatal("expected no receive only changed deleted item") } - if localSize(t, m, fcfg.ID).Deleted != 1 { + if mustV(m.LocalSize(fcfg.ID, protocol.LocalDeviceID)).Deleted != 1 { t.Fatal("expected one local deleted item") } } @@ -3679,7 +3586,7 @@ func testConfigChangeTriggersClusterConfigs(t *testing.T, expectFirst, expectSec func TestIssue6961(t *testing.T) { wcfg, fcfg, wcfgCancel := newDefaultCfgWrapper() defer wcfgCancel() - tfs := fcfg.Filesystem(nil) + tfs := fcfg.Filesystem() waiter, err := wcfg.Modify(func(cfg *config.Configuration) { cfg.SetDevice(newDeviceConfiguration(cfg.Defaults.Device, device2, "device2")) fcfg.Type = config.FolderTypeReceiveOnly @@ -3690,11 +3597,6 @@ func TestIssue6961(t *testing.T) { waiter.Wait() // Always recalc/repair when opening a fileset. m := newModel(t, wcfg, myID, nil) - m.db.Close() - m.db, err = db.NewLowlevel(backend.OpenMemory(), m.evLogger, db.WithRecheckInterval(time.Millisecond)) - if err != nil { - t.Fatal(err) - } m.ServeBackground() defer cleanupModelAndRemoveDir(m, tfs.URI()) conn1 := addFakeConn(m, device1, fcfg.ID) @@ -3749,11 +3651,9 @@ func TestIssue6961(t *testing.T) { func TestCompletionEmptyGlobal(t *testing.T) { m, conn, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) files := []protocol.FileInfo{{Name: "foo", Version: protocol.Vector{}.Update(myID.Short()), Sequence: 1}} - m.mut.Lock() - m.folderFiles[fcfg.ID].Update(protocol.LocalDeviceID, files) - m.mut.Unlock() + m.sdb.Update(fcfg.ID, protocol.LocalDeviceID, files) files[0].Deleted = true files[0].Version = files[0].Version.Update(device1.Short()) must(t, m.IndexUpdate(conn, &protocol.IndexUpdate{Folder: fcfg.ID, Files: files})) @@ -3950,7 +3850,7 @@ func TestCCFolderNotRunning(t *testing.T) { // Create the folder, but don't start it. w, fcfg, wCancel := newDefaultCfgWrapper() defer wCancel() - tfs := fcfg.Filesystem(nil) + tfs := fcfg.Filesystem() m := newModel(t, w, myID, nil) defer cleanupModelAndRemoveDir(m, tfs.URI()) @@ -3987,7 +3887,7 @@ func TestPendingFolder(t *testing.T) { Time: time.Now().Truncate(time.Second), Label: pfolder, } - if err := m.db.AddOrUpdatePendingFolder(pfolder, of, device2); err != nil { + if err := m.observed.AddOrUpdatePendingFolder(pfolder, of, device2); err != nil { t.Fatal(err) } deviceFolders, err := m.PendingFolders(protocol.EmptyDeviceID) @@ -4006,7 +3906,7 @@ func TestPendingFolder(t *testing.T) { t.Fatal(err) } setDevice(t, w, config.DeviceConfiguration{DeviceID: device3}) - if err := m.db.AddOrUpdatePendingFolder(pfolder, of, device3); err != nil { + if err := m.observed.AddOrUpdatePendingFolder(pfolder, of, device3); err != nil { t.Fatal(err) } deviceFolders, err = m.PendingFolders(device2) @@ -4057,7 +3957,7 @@ func TestDeletedNotLocallyChangedReceiveEncrypted(t *testing.T) { func deletedNotLocallyChanged(t *testing.T, ft config.FolderType) { w, fcfg, wCancel := newDefaultCfgWrapper() - tfs := fcfg.Filesystem(nil) + tfs := fcfg.Filesystem() fcfg.Type = ft setFolder(t, w, fcfg) defer wCancel() @@ -4138,3 +4038,17 @@ type modtimeTruncatingFileInfo struct { func (fi modtimeTruncatingFileInfo) ModTime() time.Time { return fi.FileInfo.ModTime().Truncate(fi.trunc) } + +func countIterator[T any](t *testing.T) func(it iter.Seq[T], errFn func() error) int { + return func(it iter.Seq[T], errFn func() error) int { + t.Helper() + count := 0 + for range it { + count++ + } + if err := errFn(); err != nil { + t.Fatal(err) + } + return count + } +} diff --git a/lib/model/progressemitter.go b/lib/model/progressemitter.go index 4a291a519..6242c33b8 100644 --- a/lib/model/progressemitter.go +++ b/lib/model/progressemitter.go @@ -76,7 +76,6 @@ func (t *ProgressEmitter) Serve(ctx context.Context) error { return nil case <-t.timer.C: t.mut.Lock() - l.Debugln("progress emitter: timer - looking after", len(t.registry)) newLastUpdated := lastUpdate newCount = t.lenRegistryLocked() @@ -94,8 +93,6 @@ func (t *ProgressEmitter) Serve(ctx context.Context) error { lastCount = newCount t.sendDownloadProgressEventLocked() progressUpdates = t.computeProgressUpdates() - } else { - l.Debugln("progress emitter: nothing new") } if newCount != 0 { @@ -247,7 +244,6 @@ func (t *ProgressEmitter) Register(s *sharedPullerState) { t.mut.Lock() defer t.mut.Unlock() if t.disabled { - l.Debugln("progress emitter: disabled, skip registering") return } l.Debugln("progress emitter: registering", s.folder, s.file.Name) @@ -266,7 +262,6 @@ func (t *ProgressEmitter) Deregister(s *sharedPullerState) { defer t.mut.Unlock() if t.disabled { - l.Debugln("progress emitter: disabled, skip deregistering") return } diff --git a/lib/model/queue.go b/lib/model/queue.go index cc68b9f85..7ca129c26 100644 --- a/lib/model/queue.go +++ b/lib/model/queue.go @@ -7,11 +7,8 @@ package model import ( - "cmp" - "slices" "time" - "github.com/syncthing/syncthing/lib/rand" "github.com/syncthing/syncthing/lib/sync" ) @@ -128,13 +125,6 @@ func (q *jobQueue) Jobs(page, perpage int) ([]string, []string, int) { return progress, queued, (page - 1) * perpage } -func (q *jobQueue) Shuffle() { - q.mut.Lock() - defer q.mut.Unlock() - - rand.Shuffle(q.queued) -} - func (q *jobQueue) Reset() { q.mut.Lock() defer q.mut.Unlock() @@ -153,39 +143,3 @@ func (q *jobQueue) lenProgress() int { defer q.mut.Unlock() return len(q.progress) } - -func (q *jobQueue) SortSmallestFirst() { - q.mut.Lock() - defer q.mut.Unlock() - - slices.SortFunc(q.queued, func(a, b jobQueueEntry) int { - return cmp.Compare(a.size, b.size) - }) -} - -func (q *jobQueue) SortLargestFirst() { - q.mut.Lock() - defer q.mut.Unlock() - - slices.SortFunc(q.queued, func(a, b jobQueueEntry) int { - return cmp.Compare(b.size, a.size) - }) -} - -func (q *jobQueue) SortOldestFirst() { - q.mut.Lock() - defer q.mut.Unlock() - - slices.SortFunc(q.queued, func(a, b jobQueueEntry) int { - return cmp.Compare(a.modified, b.modified) - }) -} - -func (q *jobQueue) SortNewestFirst() { - q.mut.Lock() - defer q.mut.Unlock() - - slices.SortFunc(q.queued, func(a, b jobQueueEntry) int { - return cmp.Compare(b.modified, a.modified) - }) -} diff --git a/lib/model/queue_test.go b/lib/model/queue_test.go index a49f2c70f..d8467ff4d 100644 --- a/lib/model/queue_test.go +++ b/lib/model/queue_test.go @@ -163,95 +163,6 @@ func TestBringToFront(t *testing.T) { } } -func TestShuffle(t *testing.T) { - q := newJobQueue() - q.Push("f1", 0, time.Time{}) - q.Push("f2", 0, time.Time{}) - q.Push("f3", 0, time.Time{}) - q.Push("f4", 0, time.Time{}) - - // This test will fail once in eight million times (1 / (4!)^5) :) - for i := 0; i < 5; i++ { - q.Shuffle() - _, queued, _ := q.Jobs(1, 100) - if l := len(queued); l != 4 { - t.Fatalf("Weird length %d returned from jobs(1, 100)", l) - } - - t.Logf("%v", queued) - if _, equal := messagediff.PrettyDiff([]string{"f1", "f2", "f3", "f4"}, queued); !equal { - // The queue was shuffled - return - } - } - - t.Error("Queue was not shuffled after five attempts.") -} - -func TestSortBySize(t *testing.T) { - q := newJobQueue() - q.Push("f1", 20, time.Time{}) - q.Push("f2", 40, time.Time{}) - q.Push("f3", 30, time.Time{}) - q.Push("f4", 10, time.Time{}) - - q.SortSmallestFirst() - - _, actual, _ := q.Jobs(1, 100) - if l := len(actual); l != 4 { - t.Fatalf("Weird length %d returned from jobs(1, 100)", l) - } - expected := []string{"f4", "f1", "f3", "f2"} - - if diff, equal := messagediff.PrettyDiff(expected, actual); !equal { - t.Errorf("SortSmallestFirst() diff:\n%s", diff) - } - - q.SortLargestFirst() - - _, actual, _ = q.Jobs(1, 100) - if l := len(actual); l != 4 { - t.Fatalf("Weird length %d returned from jobs(1, 100)", l) - } - expected = []string{"f2", "f3", "f1", "f4"} - - if diff, equal := messagediff.PrettyDiff(expected, actual); !equal { - t.Errorf("SortLargestFirst() diff:\n%s", diff) - } -} - -func TestSortByAge(t *testing.T) { - q := newJobQueue() - q.Push("f1", 0, time.Unix(20, 0)) - q.Push("f2", 0, time.Unix(40, 0)) - q.Push("f3", 0, time.Unix(30, 0)) - q.Push("f4", 0, time.Unix(10, 0)) - - q.SortOldestFirst() - - _, actual, _ := q.Jobs(1, 100) - if l := len(actual); l != 4 { - t.Fatalf("Weird length %d returned from jobs(1, 100)", l) - } - expected := []string{"f4", "f1", "f3", "f2"} - - if diff, equal := messagediff.PrettyDiff(expected, actual); !equal { - t.Errorf("SortOldestFirst() diff:\n%s", diff) - } - - q.SortNewestFirst() - - _, actual, _ = q.Jobs(1, 100) - if l := len(actual); l != 4 { - t.Fatalf("Weird length %d returned from jobs(1, 100)", l) - } - expected = []string{"f2", "f3", "f1", "f4"} - - if diff, equal := messagediff.PrettyDiff(expected, actual); !equal { - t.Errorf("SortNewestFirst() diff:\n%s", diff) - } -} - func BenchmarkJobQueueBump(b *testing.B) { files := genFiles(10000) diff --git a/lib/model/requests_test.go b/lib/model/requests_test.go index 7a32b23ae..1f48d6455 100644 --- a/lib/model/requests_test.go +++ b/lib/model/requests_test.go @@ -32,7 +32,7 @@ func TestRequestSimple(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - tfs := fcfg.Filesystem(nil) + tfs := fcfg.Filesystem() defer cleanupModelAndRemoveDir(m, tfs.URI()) // We listen for incoming index updates and trigger when we see one for @@ -80,7 +80,7 @@ func TestSymlinkTraversalRead(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) // We listen for incoming index updates and trigger when we see one for // the expected test file. @@ -123,7 +123,7 @@ func TestSymlinkTraversalWrite(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) // We listen for incoming index updates and trigger when we see one for // the expected names. @@ -182,7 +182,7 @@ func TestRequestCreateTmpSymlink(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) // We listen for incoming index updates and trigger when we see one for // the expected test file. @@ -229,7 +229,7 @@ func pullInvalidIgnored(t *testing.T, ft config.FolderType) { w, wCancel := newConfigWrapper(defaultCfgWrapper.RawCopy()) defer wCancel() fcfg := w.FolderList()[0] - fss := fcfg.Filesystem(nil) + fss := fcfg.Filesystem() fcfg.Type = ft setFolder(t, w, fcfg) m := setupModel(t, w) @@ -358,7 +358,7 @@ func pullInvalidIgnored(t *testing.T, ft config.FolderType) { func TestIssue4841(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) received := make(chan []protocol.FileInfo) fc.setIndexFn(func(_ context.Context, _ string, fs []protocol.FileInfo) error { @@ -407,7 +407,7 @@ func TestIssue4841(t *testing.T) { func TestRescanIfHaveInvalidContent(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - tfs := fcfg.Filesystem(nil) + tfs := fcfg.Filesystem() defer cleanupModelAndRemoveDir(m, tfs.URI()) payload := []byte("hello") @@ -436,11 +436,8 @@ func TestRescanIfHaveInvalidContent(t *testing.T) { } f := checkReceived(<-received) - if f.Blocks[0].WeakHash != 103547413 { - t.Fatalf("unexpected weak hash: %d != 103547413", f.Blocks[0].WeakHash) - } - res, err := m.Request(device1Conn, &protocol.Request{Folder: "default", Name: "foo", Size: len(payload), Hash: f.Blocks[0].Hash, WeakHash: f.Blocks[0].WeakHash}) + res, err := m.Request(device1Conn, &protocol.Request{Folder: "default", Name: "foo", Size: len(payload), Hash: f.Blocks[0].Hash}) if err != nil { t.Fatal(err) } @@ -454,26 +451,25 @@ func TestRescanIfHaveInvalidContent(t *testing.T) { writeFile(t, tfs, "foo", payload) - _, err = m.Request(device1Conn, &protocol.Request{Folder: "default", Name: "foo", Size: len(payload), Hash: f.Blocks[0].Hash, WeakHash: f.Blocks[0].WeakHash}) + _, err = m.Request(device1Conn, &protocol.Request{Folder: "default", Name: "foo", Size: len(payload), Hash: f.Blocks[0].Hash}) if err == nil { t.Fatalf("expected failure") } select { case fs := <-received: - f := checkReceived(fs) - if f.Blocks[0].WeakHash != 41943361 { - t.Fatalf("unexpected weak hash: %d != 41943361", f.Blocks[0].WeakHash) - } + checkReceived(fs) case <-time.After(time.Second): t.Fatalf("timed out") } } func TestParentDeletion(t *testing.T) { + t.Skip("flaky") + m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - testFs := fcfg.Filesystem(nil) + testFs := fcfg.Filesystem() defer cleanupModelAndRemoveDir(m, testFs.URI()) parent := "foo" @@ -552,7 +548,7 @@ func TestRequestSymlinkWindows(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) received := make(chan []protocol.FileInfo) fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error { @@ -629,7 +625,7 @@ func TestRequestRemoteRenameChanged(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - tfs := fcfg.Filesystem(nil) + tfs := fcfg.Filesystem() defer cleanupModel(m) received := make(chan []protocol.FileInfo) @@ -762,7 +758,7 @@ func TestRequestRemoteRenameChanged(t *testing.T) { func TestRequestRemoteRenameConflict(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - tfs := fcfg.Filesystem(nil) + tfs := fcfg.Filesystem() defer cleanupModel(m) recv := make(chan int) @@ -852,7 +848,7 @@ func TestRequestRemoteRenameConflict(t *testing.T) { func TestRequestDeleteChanged(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - tfs := fcfg.Filesystem(nil) + tfs := fcfg.Filesystem() defer cleanupModelAndRemoveDir(m, tfs.URI()) done := make(chan struct{}) @@ -966,7 +962,7 @@ func TestIgnoreDeleteUnignore(t *testing.T) { w, fcfg, wCancel := newDefaultCfgWrapper() defer wCancel() m := setupModel(t, w) - fss := fcfg.Filesystem(nil) + fss := fcfg.Filesystem() defer cleanupModel(m) folderIgnoresAlwaysReload(t, m, fcfg) @@ -1060,7 +1056,7 @@ func TestIgnoreDeleteUnignore(t *testing.T) { func TestRequestLastFileProgress(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - tfs := fcfg.Filesystem(nil) + tfs := fcfg.Filesystem() defer cleanupModelAndRemoveDir(m, tfs.URI()) done := make(chan struct{}) @@ -1095,7 +1091,7 @@ func TestRequestIndexSenderPause(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - tfs := fcfg.Filesystem(nil) + tfs := fcfg.Filesystem() defer cleanupModelAndRemoveDir(m, tfs.URI()) indexChan := make(chan []protocol.FileInfo) @@ -1208,7 +1204,7 @@ func TestRequestIndexSenderPause(t *testing.T) { func TestRequestIndexSenderClusterConfigBeforeStart(t *testing.T) { w, fcfg, wCancel := newDefaultCfgWrapper() defer wCancel() - tfs := fcfg.Filesystem(nil) + tfs := fcfg.Filesystem() dir1 := "foo" dir2 := "bar" @@ -1223,7 +1219,7 @@ func TestRequestIndexSenderClusterConfigBeforeStart(t *testing.T) { // Add connection (sends incoming cluster config) before starting the new model m = &testModel{ - model: NewModel(m.cfg, m.id, m.db, m.protectedFiles, m.evLogger, protocol.NewKeyGenerator()).(*model), + model: NewModel(m.cfg, m.id, m.sdb, m.protectedFiles, m.evLogger, protocol.NewKeyGenerator()).(*model), evCancel: m.evCancel, stopped: make(chan struct{}), } @@ -1275,7 +1271,7 @@ func TestRequestReceiveEncrypted(t *testing.T) { w, fcfg, wCancel := newDefaultCfgWrapper() defer wCancel() - tfs := fcfg.Filesystem(nil) + tfs := fcfg.Filesystem() fcfg.Type = config.FolderTypeReceiveEncrypted setFolder(t, w, fcfg) @@ -1287,10 +1283,7 @@ func TestRequestReceiveEncrypted(t *testing.T) { files := genFiles(2) files[1].LocalFlags = protocol.FlagLocalReceiveOnly - m.mut.RLock() - fset := m.folderFiles[fcfg.ID] - m.mut.RUnlock() - fset.Update(protocol.LocalDeviceID, files) + m.sdb.Update(fcfg.ID, protocol.LocalDeviceID, files) indexChan := make(chan []protocol.FileInfo, 10) done := make(chan struct{}) @@ -1382,7 +1375,7 @@ func TestRequestGlobalInvalidToValid(t *testing.T) { must(t, err) waiter.Wait() conn := addFakeConn(m, device2, fcfg.ID) - tfs := fcfg.Filesystem(nil) + tfs := fcfg.Filesystem() defer cleanupModelAndRemoveDir(m, tfs.URI()) indexChan := make(chan []protocol.FileInfo, 1) @@ -1408,7 +1401,7 @@ func TestRequestGlobalInvalidToValid(t *testing.T) { file.SetIgnored() m.IndexUpdate(conn, &protocol.IndexUpdate{Folder: fcfg.ID, Files: []protocol.FileInfo{prepareFileInfoForIndex(file)}}) - // Wait for the ignored file to be received and possible pulled + // Wait for the ignored file to be received and possibly pulled timeout := time.After(10 * time.Second) globalUpdated := false for { @@ -1428,13 +1421,9 @@ func TestRequestGlobalInvalidToValid(t *testing.T) { } globalUpdated = true } - snap, err := m.DBSnapshot(fcfg.ID) - if err != nil { + if s, err := m.NeedSize(fcfg.ID, protocol.LocalDeviceID); err != nil { t.Fatal(err) - } - need := snap.NeedSize(protocol.LocalDeviceID) - snap.Release() - if need.Files == 0 { + } else if s.Files == 0 { break } } diff --git a/lib/model/sentdownloadstate.go b/lib/model/sentdownloadstate.go index 2c205a7ad..e5723dd4d 100644 --- a/lib/model/sentdownloadstate.go +++ b/lib/model/sentdownloadstate.go @@ -97,7 +97,7 @@ func (s *sentFolderDownloadState) update(pullers []*sharedPullerState) []protoco localFile.updated = pullerBlockIndexesUpdated localFile.version = pullerVersion localFile.created = pullerCreated - localFile.blockSize = int(pullerBlockSize) + localFile.blockSize = pullerBlockSize continue } diff --git a/lib/model/service_map.go b/lib/model/service_map.go index 7eaf94506..cc681e9d1 100644 --- a/lib/model/service_map.go +++ b/lib/model/service_map.go @@ -8,6 +8,7 @@ package model import ( "context" + "errors" "fmt" "time" @@ -16,7 +17,7 @@ import ( "github.com/thejerf/suture/v4" ) -var errSvcNotFound = fmt.Errorf("service not found") +var errSvcNotFound = errors.New("service not found") // A serviceMap is a utility map of arbitrary keys to a suture.Service of // some kind, where adding and removing services ensures they are properly @@ -65,7 +66,6 @@ func (s *serviceMap[K, S]) Stop(k K) { if tok, ok := s.tokens[k]; ok { s.supervisor.Remove(tok) } - return } // StopAndWaitChan removes the service at the given key from the supervisor, diff --git a/lib/model/sharedpullerstate.go b/lib/model/sharedpullerstate.go index 70b82dc31..f0b66f685 100644 --- a/lib/model/sharedpullerstate.go +++ b/lib/model/sharedpullerstate.go @@ -285,15 +285,6 @@ func (s *sharedPullerState) skippedSparseBlock(bytes int) { metricFolderProcessedBytesTotal.WithLabelValues(s.folder, metricSourceSkipped).Add(float64(bytes)) } -func (s *sharedPullerState) copiedFromOriginShifted(bytes int) { - s.mut.Lock() - s.copyOrigin++ - s.copyOriginShifted++ - s.updated = time.Now() - s.mut.Unlock() - metricFolderProcessedBytesTotal.WithLabelValues(s.folder, metricSourceLocalShifted).Add(float64(bytes)) -} - func (s *sharedPullerState) pullStarted() { s.mut.Lock() s.copyTotal-- @@ -393,7 +384,7 @@ func writeEncryptionTrailer(file protocol.FileInfo, writer io.WriterAt) (int64, if err != nil { return 0, err } - binary.BigEndian.PutUint32(bs[n:], uint32(n)) + binary.BigEndian.PutUint32(bs[n:], uint32(n)) //nolint:gosec bs = bs[:n+4] if _, err := writer.WriteAt(bs, wireFile.Size); err != nil { diff --git a/lib/model/testos_test.go b/lib/model/testos_test.go index 2c56265b6..b7110ef75 100644 --- a/lib/model/testos_test.go +++ b/lib/model/testos_test.go @@ -6,10 +6,6 @@ package model -import ( - "github.com/syncthing/syncthing/lib/fs" -) - // fatal is the required common interface between *testing.B and *testing.T type fatal interface { Fatal(...interface{}) @@ -23,9 +19,9 @@ func must(f fatal, err error) { } } -func mustRemove(f fatal, err error) { - f.Helper() - if err != nil && !fs.IsNotExist(err) { - f.Fatal(err) +func mustV[T any](v T, err error) T { + if err != nil { + panic(err) } + return v } diff --git a/lib/model/testutils_test.go b/lib/model/testutils_test.go index 1e203f9fd..8d6140ec5 100644 --- a/lib/model/testutils_test.go +++ b/lib/model/testutils_test.go @@ -12,9 +12,8 @@ import ( "testing" "time" + "github.com/syncthing/syncthing/internal/db/sqlite" "github.com/syncthing/syncthing/lib/config" - "github.com/syncthing/syncthing/lib/db" - "github.com/syncthing/syncthing/lib/db/backend" "github.com/syncthing/syncthing/lib/events" "github.com/syncthing/syncthing/lib/fs" "github.com/syncthing/syncthing/lib/ignore" @@ -104,6 +103,7 @@ func newDefaultCfgWrapper() (config.Wrapper, config.FolderConfiguration, context func newFolderConfig() config.FolderConfiguration { cfg := newFolderConfiguration(defaultCfgWrapper, "default", "default", config.FilesystemTypeFake, rand.String(32)+"?content=true") cfg.FSWatcherEnabled = false + cfg.PullerDelayS = 0 cfg.Devices = append(cfg.Devices, config.FolderDeviceConfiguration{DeviceID: device1}) return cfg } @@ -149,11 +149,14 @@ type testModel struct { func newModel(t testing.TB, cfg config.Wrapper, id protocol.DeviceID, protectedFiles []string) *testModel { t.Helper() evLogger := events.NewLogger() - ldb, err := db.NewLowlevel(backend.OpenMemory(), evLogger) + mdb, err := sqlite.OpenTemp() if err != nil { t.Fatal(err) } - m := NewModel(cfg, id, ldb, protectedFiles, evLogger, protocol.NewKeyGenerator()).(*model) + t.Cleanup(func() { + mdb.Close() + }) + m := NewModel(cfg, id, mdb, protectedFiles, evLogger, protocol.NewKeyGenerator()).(*model) ctx, cancel := context.WithCancel(context.Background()) go evLogger.Serve(ctx) return &testModel{ @@ -174,12 +177,6 @@ func (m *testModel) ServeBackground() { <-m.started } -func (m *testModel) testAvailability(folder string, file protocol.FileInfo, block protocol.BlockInfo) []Availability { - av, err := m.model.Availability(folder, file, block) - must(m.t, err) - return av -} - func (m *testModel) testCurrentFolderFile(folder string, file string) (protocol.FileInfo, bool) { f, ok, err := m.model.CurrentFolderFile(folder, file) must(m.t, err) @@ -198,7 +195,7 @@ func cleanupModel(m *testModel) { <-m.stopped } m.evCancel() - m.db.Close() + m.sdb.Close() os.Remove(m.cfg.ConfigPath()) } @@ -240,52 +237,6 @@ func (*alwaysChanged) Changed() bool { return true } -func localSize(t *testing.T, m Model, folder string) db.Counts { - t.Helper() - snap := dbSnapshot(t, m, folder) - defer snap.Release() - return snap.LocalSize() -} - -func globalSize(t *testing.T, m Model, folder string) db.Counts { - t.Helper() - snap := dbSnapshot(t, m, folder) - defer snap.Release() - return snap.GlobalSize() -} - -func receiveOnlyChangedSize(t *testing.T, m Model, folder string) db.Counts { - t.Helper() - snap := dbSnapshot(t, m, folder) - defer snap.Release() - return snap.ReceiveOnlyChangedSize() -} - -func needSizeLocal(t *testing.T, m Model, folder string) db.Counts { - t.Helper() - snap := dbSnapshot(t, m, folder) - defer snap.Release() - return snap.NeedSize(protocol.LocalDeviceID) -} - -func dbSnapshot(t *testing.T, m Model, folder string) *db.Snapshot { - t.Helper() - snap, err := m.DBSnapshot(folder) - if err != nil { - t.Fatal(err) - } - return snap -} - -func fsetSnapshot(t *testing.T, fset *db.FileSet) *db.Snapshot { - t.Helper() - snap, err := fset.Snapshot() - if err != nil { - t.Fatal(err) - } - return snap -} - // Reach in and update the ignore matcher to one that always does // reloads when asked to, instead of checking file mtimes. This is // because we will be changing the files on disk often enough that the @@ -293,10 +244,9 @@ func fsetSnapshot(t *testing.T, fset *db.FileSet) *db.Snapshot { func folderIgnoresAlwaysReload(t testing.TB, m *testModel, fcfg config.FolderConfiguration) { t.Helper() m.removeFolder(fcfg) - fset := newFileSet(t, fcfg.ID, m.db) - ignores := ignore.New(fcfg.Filesystem(nil), ignore.WithCache(true), ignore.WithChangeDetector(newAlwaysChanged())) + ignores := ignore.New(fcfg.Filesystem(), ignore.WithCache(true), ignore.WithChangeDetector(newAlwaysChanged())) m.mut.Lock() - m.addAndStartFolderLockedWithIgnores(fcfg, fset, ignores) + m.addAndStartFolderLockedWithIgnores(fcfg, ignores) m.mut.Unlock() } @@ -319,12 +269,11 @@ func basicClusterConfig(local, remote protocol.DeviceID, folders ...string) *pro } func localIndexUpdate(m *testModel, folder string, fs []protocol.FileInfo) { - m.mut.RLock() - fset := m.folderFiles[folder] - m.mut.RUnlock() - - fset.Update(protocol.LocalDeviceID, fs) - seq := fset.Sequence(protocol.LocalDeviceID) + m.sdb.Update(folder, protocol.LocalDeviceID, fs) + seq, err := m.sdb.GetDeviceSequence(folder, protocol.LocalDeviceID) + if err != nil { + panic(err) + } filenames := make([]string, len(fs)) for i, file := range fs { filenames[i] = file.Name @@ -345,15 +294,6 @@ func newDeviceConfiguration(defaultCfg config.DeviceConfiguration, id protocol.D return cfg } -func newFileSet(t testing.TB, folder string, ldb *db.Lowlevel) *db.FileSet { - t.Helper() - fset, err := db.NewFileSet(folder, ldb) - if err != nil { - t.Fatal(err) - } - return fset -} - func replace(t testing.TB, w config.Wrapper, to config.Configuration) { t.Helper() waiter, err := w.Modify(func(cfg *config.Configuration) { diff --git a/lib/protocol/bep_fileinfo.go b/lib/protocol/bep_fileinfo.go index fa5f78d5d..7925d14d6 100644 --- a/lib/protocol/bep_fileinfo.go +++ b/lib/protocol/bep_fileinfo.go @@ -19,10 +19,12 @@ import ( // FileInfo.LocalFlags flags const ( - FlagLocalUnsupported = 1 << 0 // The kind is unsupported, e.g. symlinks on Windows - FlagLocalIgnored = 1 << 1 // Matches local ignore patterns - FlagLocalMustRescan = 1 << 2 // Doesn't match content on disk, must be rechecked fully - FlagLocalReceiveOnly = 1 << 3 // Change detected on receive only folder + FlagLocalUnsupported = 1 << 0 // 1: The kind is unsupported, e.g. symlinks on Windows + FlagLocalIgnored = 1 << 1 // 2: Matches local ignore patterns + FlagLocalMustRescan = 1 << 2 // 4: Doesn't match content on disk, must be rechecked fully + FlagLocalReceiveOnly = 1 << 3 // 8: Change detected on receive only folder + FlagLocalGlobal = 1 << 4 // 16: This is the global file version + FlagLocalNeeded = 1 << 5 // 32: We need this file // Flags that should result in the Invalid bit on outgoing updates LocalInvalidFlags = FlagLocalUnsupported | FlagLocalIgnored | FlagLocalMustRescan | FlagLocalReceiveOnly @@ -32,7 +34,7 @@ const ( // disk. LocalConflictFlags = FlagLocalUnsupported | FlagLocalIgnored | FlagLocalReceiveOnly - LocalAllFlags = FlagLocalUnsupported | FlagLocalIgnored | FlagLocalMustRescan | FlagLocalReceiveOnly + LocalAllFlags = FlagLocalUnsupported | FlagLocalIgnored | FlagLocalMustRescan | FlagLocalReceiveOnly | FlagLocalGlobal | FlagLocalNeeded ) // BlockSizes is the list of valid block sizes, from min to max @@ -544,32 +546,29 @@ func (f *FileInfo) setNoContent() { } type BlockInfo struct { - Hash []byte - Offset int64 - Size int - WeakHash uint32 + Hash []byte + Offset int64 + Size int } func (b BlockInfo) ToWire() *bep.BlockInfo { return &bep.BlockInfo{ - Hash: b.Hash, - Offset: b.Offset, - Size: int32(b.Size), - WeakHash: b.WeakHash, + Hash: b.Hash, + Offset: b.Offset, + Size: int32(b.Size), } } func BlockInfoFromWire(w *bep.BlockInfo) BlockInfo { return BlockInfo{ - Hash: w.Hash, - Offset: w.Offset, - Size: int(w.Size), - WeakHash: w.WeakHash, + Hash: w.Hash, + Offset: w.Offset, + Size: int(w.Size), } } func (b BlockInfo) String() string { - return fmt.Sprintf("Block{%d/%d/%d/%x}", b.Offset, b.Size, b.WeakHash, b.Hash) + return fmt.Sprintf("Block{%d/%d/%x}", b.Offset, b.Size, b.Hash) } // For each block size, the hash of a block of all zeroes @@ -596,7 +595,6 @@ func BlocksHash(bs []BlockInfo) []byte { h := sha256.New() for _, b := range bs { _, _ = h.Write(b.Hash) - _ = binary.Write(h, binary.BigEndian, b.WeakHash) } return h.Sum(nil) } diff --git a/lib/protocol/bep_request_response.go b/lib/protocol/bep_request_response.go index 2affd3724..db0722791 100644 --- a/lib/protocol/bep_request_response.go +++ b/lib/protocol/bep_request_response.go @@ -25,7 +25,6 @@ type Request struct { Size int Hash []byte FromTemporary bool - WeakHash uint32 BlockNo int } @@ -38,7 +37,6 @@ func (r *Request) toWire() *bep.Request { Size: int32(r.Size), Hash: r.Hash, FromTemporary: r.FromTemporary, - WeakHash: r.WeakHash, BlockNo: int32(r.BlockNo), } } @@ -52,7 +50,6 @@ func requestFromWire(w *bep.Request) *Request { Size: int(w.Size), Hash: w.Hash, FromTemporary: w.FromTemporary, - WeakHash: w.WeakHash, BlockNo: int(w.BlockNo), } } diff --git a/lib/protocol/bufferpool.go b/lib/protocol/bufferpool.go index cf7b797b6..fa8722d65 100644 --- a/lib/protocol/bufferpool.go +++ b/lib/protocol/bufferpool.go @@ -59,7 +59,7 @@ func (p *bufferPool) Get(size int) []byte { } // Put makes the given byte slice available again in the global pool. -// You must only Put() slices that were returned by Get() or Upgrade(). +// You must only Put() slices that were returned by Get(). func (p *bufferPool) Put(bs []byte) { // Don't buffer slices outside of our pool range if cap(bs) > MaxBlockSize || cap(bs) < MinBlockSize { @@ -72,20 +72,6 @@ func (p *bufferPool) Put(bs []byte) { p.pools[bkt].Put(&bs) } -// Upgrade grows the buffer to the requested size, while attempting to reuse -// it if possible. -func (p *bufferPool) Upgrade(bs []byte, size int) []byte { - if cap(bs) >= size { - // Reslicing is enough, lets go! - return bs[:size] - } - - // It was too small. But it pack into the pool and try to get another - // buffer. - p.Put(bs) - return p.Get(size) -} - // getBucketForLen returns the bucket where we should get a slice of a // certain length. Each bucket is guaranteed to hold slices that are // precisely the block size for that bucket, so if the block size is larger diff --git a/lib/protocol/common_test.go b/lib/protocol/common_test.go index 74200df10..b6e25c2b9 100644 --- a/lib/protocol/common_test.go +++ b/lib/protocol/common_test.go @@ -17,7 +17,6 @@ type TestModel struct { offset int64 size int32 hash []byte - weakHash uint32 fromTemporary bool indexFn func(string, []FileInfo) ccFn func(*ClusterConfig) @@ -48,7 +47,6 @@ func (t *TestModel) Request(_ Connection, req *Request) (RequestResponse, error) t.offset = req.Offset t.size = int32(req.Size) t.hash = req.Hash - t.weakHash = req.WeakHash t.fromTemporary = req.FromTemporary buf := make([]byte, len(t.data)) copy(buf, t.data) diff --git a/lib/protocol/protocol.go b/lib/protocol/protocol.go index de962eeea..c4398a5cc 100644 --- a/lib/protocol/protocol.go +++ b/lib/protocol/protocol.go @@ -526,8 +526,9 @@ func (c *rawConnection) readMessageAfterHeader(hdr *bep.Header, fourByteBuf []by // Then comes the message buf := BufferPool.Get(int(msgLen)) + defer BufferPool.Put(buf) + if _, err := io.ReadFull(c.cr, buf); err != nil { - BufferPool.Put(buf) return nil, fmt.Errorf("reading message: %w", err) } @@ -539,7 +540,6 @@ func (c *rawConnection) readMessageAfterHeader(hdr *bep.Header, fourByteBuf []by case bep.MessageCompression_MESSAGE_COMPRESSION_LZ4: decomp, err := lz4Decompress(buf) - BufferPool.Put(buf) if err != nil { return nil, fmt.Errorf("decompressing message: %w", err) } @@ -555,14 +555,11 @@ func (c *rawConnection) readMessageAfterHeader(hdr *bep.Header, fourByteBuf []by msg, err := newMessage(hdr.Type) if err != nil { - BufferPool.Put(buf) return nil, err } if err := proto.Unmarshal(buf, msg); err != nil { - BufferPool.Put(buf) return nil, fmt.Errorf("unmarshalling message: %w", err) } - BufferPool.Put(buf) return msg, nil } @@ -581,16 +578,16 @@ func (c *rawConnection) readHeader(fourByteBuf []byte) (*bep.Header, error) { // Then comes the header buf := BufferPool.Get(int(hdrLen)) + defer BufferPool.Put(buf) + if _, err := io.ReadFull(c.cr, buf); err != nil { - BufferPool.Put(buf) return nil, fmt.Errorf("reading header: %w", err) } var hdr bep.Header err := proto.Unmarshal(buf, &hdr) - BufferPool.Put(buf) if err != nil { - return nil, fmt.Errorf("unmarshalling header: %w %x", err, buf) + return nil, fmt.Errorf("unmarshalling header: %w", err) } metricDeviceRecvDecompressedBytes.WithLabelValues(c.idString).Add(float64(2 + len(buf))) diff --git a/lib/protocol/protocol_test.go b/lib/protocol/protocol_test.go index 94a4ac2fc..e02649411 100644 --- a/lib/protocol/protocol_test.go +++ b/lib/protocol/protocol_test.go @@ -10,10 +10,8 @@ import ( "bytes" "context" "encoding/hex" - "encoding/json" "errors" "io" - "os" "sync" "testing" "time" @@ -280,28 +278,6 @@ func TestUnmarshalFDPUv16v17(t *testing.T) { } } -func testMarshal(t *testing.T, prefix string, m1, m2 proto.Message) bool { - buf, err := proto.Marshal(m1) - if err != nil { - t.Fatal(err) - } - - err = proto.Unmarshal(buf, m2) - if err != nil { - t.Fatal(err) - } - - bs1, _ := json.MarshalIndent(m1, "", " ") - bs2, _ := json.MarshalIndent(m2, "", " ") - if !bytes.Equal(bs1, bs2) { - os.WriteFile(prefix+"-1.txt", bs1, 0o644) - os.WriteFile(prefix+"-2.txt", bs2, 0o644) - return false - } - - return true -} - func TestWriteCompressed(t *testing.T) { for _, random := range []bool{false, true} { buf := new(bytes.Buffer) diff --git a/lib/protocol/vector.go b/lib/protocol/vector.go index 30cce4231..adaaa7494 100644 --- a/lib/protocol/vector.go +++ b/lib/protocol/vector.go @@ -7,6 +7,11 @@ package protocol import ( + "encoding/binary" + "encoding/hex" + "fmt" + "strconv" + "strings" "time" "github.com/syncthing/syncthing/internal/gen/bep" @@ -20,6 +25,19 @@ type Vector struct { Counters []Counter } +func (v *Vector) String() string { + var buf strings.Builder + for i, c := range v.Counters { + if i > 0 { + buf.WriteRune(',') + } + var idbs [8]byte + binary.BigEndian.PutUint64(idbs[:], uint64(c.ID)) + fmt.Fprintf(&buf, "%x:%d", idbs, c.Value) + } + return buf.String() +} + func (v *Vector) ToWire() *bep.Vector { counters := make([]*bep.Counter, len(v.Counters)) for i, c := range v.Counters { @@ -42,6 +60,31 @@ func VectorFromWire(w *bep.Vector) Vector { return v } +func VectorFromString(s string) (Vector, error) { + pairs := strings.Split(s, ",") + var v Vector + v.Counters = make([]Counter, len(pairs)) + for i, pair := range pairs { + idStr, valStr, ok := strings.Cut(pair, ":") + if !ok { + return Vector{}, fmt.Errorf("bad pair %q", pair) + } + idslice, err := hex.DecodeString(idStr) + if err != nil { + return Vector{}, fmt.Errorf("bad id in pair %q", pair) + } + var idbs [8]byte + copy(idbs[8-len(idslice):], idslice) + id := binary.BigEndian.Uint64(idbs[:]) + val, err := strconv.ParseUint(valStr, 10, 64) + if err != nil { + return Vector{}, fmt.Errorf("bad val in pair %q", pair) + } + v.Counters[i] = Counter{ID: ShortID(id), Value: val} + } + return v, nil +} + // Counter represents a single counter in the version vector. type Counter struct { ID ShortID diff --git a/lib/relay/protocol/packets_xdr.go b/lib/relay/protocol/packets_xdr.go index fb0903b62..0477c066b 100644 --- a/lib/relay/protocol/packets_xdr.go +++ b/lib/relay/protocol/packets_xdr.go @@ -31,7 +31,7 @@ struct header { */ -func (header) XDRSize() int { +func (o header) XDRSize() int { return 4 + 4 + 4 } @@ -60,7 +60,6 @@ func (o *header) UnmarshalXDR(bs []byte) error { u := &xdr.Unmarshaller{Data: bs} return o.UnmarshalXDRFrom(u) } - func (o *header) UnmarshalXDRFrom(u *xdr.Unmarshaller) error { o.magic = u.UnmarshalUint32() o.messageType = int32(u.UnmarshalUint32()) @@ -79,27 +78,26 @@ struct Ping { */ -func (Ping) XDRSize() int { +func (o Ping) XDRSize() int { return 0 } - -func (Ping) MarshalXDR() ([]byte, error) { +func (o Ping) MarshalXDR() ([]byte, error) { return nil, nil } -func (Ping) MustMarshalXDR() []byte { +func (o Ping) MustMarshalXDR() []byte { return nil } -func (Ping) MarshalXDRInto(_ *xdr.Marshaller) error { +func (o Ping) MarshalXDRInto(m *xdr.Marshaller) error { return nil } -func (*Ping) UnmarshalXDR(_ []byte) error { +func (o *Ping) UnmarshalXDR(bs []byte) error { return nil } -func (*Ping) UnmarshalXDRFrom(_ *xdr.Unmarshaller) error { +func (o *Ping) UnmarshalXDRFrom(u *xdr.Unmarshaller) error { return nil } @@ -114,27 +112,26 @@ struct Pong { */ -func (Pong) XDRSize() int { +func (o Pong) XDRSize() int { return 0 } - -func (Pong) MarshalXDR() ([]byte, error) { +func (o Pong) MarshalXDR() ([]byte, error) { return nil, nil } -func (Pong) MustMarshalXDR() []byte { +func (o Pong) MustMarshalXDR() []byte { return nil } -func (Pong) MarshalXDRInto(_ *xdr.Marshaller) error { +func (o Pong) MarshalXDRInto(m *xdr.Marshaller) error { return nil } -func (*Pong) UnmarshalXDR(_ []byte) error { +func (o *Pong) UnmarshalXDR(bs []byte) error { return nil } -func (*Pong) UnmarshalXDRFrom(_ *xdr.Unmarshaller) error { +func (o *Pong) UnmarshalXDRFrom(u *xdr.Unmarshaller) error { return nil } @@ -149,27 +146,26 @@ struct RelayFull { */ -func (RelayFull) XDRSize() int { +func (o RelayFull) XDRSize() int { return 0 } - -func (RelayFull) MarshalXDR() ([]byte, error) { +func (o RelayFull) MarshalXDR() ([]byte, error) { return nil, nil } -func (RelayFull) MustMarshalXDR() []byte { +func (o RelayFull) MustMarshalXDR() []byte { return nil } -func (RelayFull) MarshalXDRInto(_ *xdr.Marshaller) error { +func (o RelayFull) MarshalXDRInto(m *xdr.Marshaller) error { return nil } -func (*RelayFull) UnmarshalXDR(_ []byte) error { +func (o *RelayFull) UnmarshalXDR(bs []byte) error { return nil } -func (*RelayFull) UnmarshalXDRFrom(_ *xdr.Unmarshaller) error { +func (o *RelayFull) UnmarshalXDRFrom(u *xdr.Unmarshaller) error { return nil } @@ -219,7 +215,6 @@ func (o *JoinRelayRequest) UnmarshalXDR(bs []byte) error { u := &xdr.Unmarshaller{Data: bs} return o.UnmarshalXDRFrom(u) } - func (o *JoinRelayRequest) UnmarshalXDRFrom(u *xdr.Unmarshaller) error { o.Token = u.UnmarshalString() return u.Error @@ -274,7 +269,6 @@ func (o *JoinSessionRequest) UnmarshalXDR(bs []byte) error { u := &xdr.Unmarshaller{Data: bs} return o.UnmarshalXDRFrom(u) } - func (o *JoinSessionRequest) UnmarshalXDRFrom(u *xdr.Unmarshaller) error { o.Key = u.UnmarshalBytesMax(32) return u.Error @@ -331,7 +325,6 @@ func (o *Response) UnmarshalXDR(bs []byte) error { u := &xdr.Unmarshaller{Data: bs} return o.UnmarshalXDRFrom(u) } - func (o *Response) UnmarshalXDRFrom(u *xdr.Unmarshaller) error { o.Code = int32(u.UnmarshalUint32()) o.Message = u.UnmarshalString() @@ -387,7 +380,6 @@ func (o *ConnectRequest) UnmarshalXDR(bs []byte) error { u := &xdr.Unmarshaller{Data: bs} return o.UnmarshalXDRFrom(u) } - func (o *ConnectRequest) UnmarshalXDRFrom(u *xdr.Unmarshaller) error { o.ID = u.UnmarshalBytesMax(32) return u.Error @@ -470,7 +462,6 @@ func (o *SessionInvitation) UnmarshalXDR(bs []byte) error { u := &xdr.Unmarshaller{Data: bs} return o.UnmarshalXDRFrom(u) } - func (o *SessionInvitation) UnmarshalXDRFrom(u *xdr.Unmarshaller) error { o.From = u.UnmarshalBytesMax(32) o.Key = u.UnmarshalBytesMax(32) diff --git a/lib/scanner/blockqueue.go b/lib/scanner/blockqueue.go index 911b482f0..8182ef74d 100644 --- a/lib/scanner/blockqueue.go +++ b/lib/scanner/blockqueue.go @@ -16,7 +16,7 @@ import ( ) // HashFile hashes the files and returns a list of blocks representing the file. -func HashFile(ctx context.Context, folderID string, fs fs.Filesystem, path string, blockSize int, counter Counter, useWeakHashes bool) ([]protocol.BlockInfo, error) { +func HashFile(ctx context.Context, folderID string, fs fs.Filesystem, path string, blockSize int, counter Counter) ([]protocol.BlockInfo, error) { fd, err := fs.Open(path) if err != nil { l.Debugln("open:", err) @@ -36,7 +36,7 @@ func HashFile(ctx context.Context, folderID string, fs fs.Filesystem, path strin // Hash the file. This may take a while for large files. - blocks, err := Blocks(ctx, fd, blockSize, size, counter, useWeakHashes) + blocks, err := Blocks(ctx, fd, blockSize, size, counter) if err != nil { l.Debugln("blocks:", err) return nil, err @@ -108,7 +108,7 @@ func (ph *parallelHasher) hashFiles(ctx context.Context) { panic("Bug. Asked to hash a directory or a deleted file.") } - blocks, err := HashFile(ctx, ph.folderID, ph.fs, f.Name, f.BlockSize(), ph.counter, true) + blocks, err := HashFile(ctx, ph.folderID, ph.fs, f.Name, f.BlockSize(), ph.counter) if err != nil { handleError(ctx, "hashing", f.Name, err, ph.outbox) continue diff --git a/lib/scanner/blocks.go b/lib/scanner/blocks.go index d875d1e19..e299ca359 100644 --- a/lib/scanner/blocks.go +++ b/lib/scanner/blocks.go @@ -10,8 +10,6 @@ import ( "bytes" "context" "crypto/sha256" - "hash" - "hash/adler32" "io" "github.com/syncthing/syncthing/lib/protocol" @@ -24,7 +22,7 @@ type Counter interface { } // Blocks returns the blockwise hash of the reader. -func Blocks(ctx context.Context, r io.Reader, blocksize int, sizehint int64, counter Counter, useWeakHashes bool) ([]protocol.BlockInfo, error) { +func Blocks(ctx context.Context, r io.Reader, blocksize int, sizehint int64, counter Counter) ([]protocol.BlockInfo, error) { if counter == nil { counter = &noopCounter{} } @@ -32,15 +30,6 @@ func Blocks(ctx context.Context, r io.Reader, blocksize int, sizehint int64, cou hf := sha256.New() const hashLength = sha256.Size - var weakHf hash.Hash32 = noopHash{} - var multiHf io.Writer = hf - if useWeakHashes { - // Use an actual weak hash function, make the multiHf - // write to both hash functions. - weakHf = adler32.New() - multiHf = io.MultiWriter(hf, weakHf) - } - var blocks []protocol.BlockInfo var hashes, thisHash []byte @@ -70,7 +59,7 @@ func Blocks(ctx context.Context, r io.Reader, blocksize int, sizehint int64, cou } lr.N = int64(blocksize) - n, err := io.CopyBuffer(multiHf, lr, buf) + n, err := io.CopyBuffer(hf, lr, buf) if err != nil { return nil, err } @@ -87,17 +76,15 @@ func Blocks(ctx context.Context, r io.Reader, blocksize int, sizehint int64, cou thisHash, hashes = hashes[:hashLength], hashes[hashLength:] b := protocol.BlockInfo{ - Size: int(n), - Offset: offset, - Hash: thisHash, - WeakHash: weakHf.Sum32(), + Size: int(n), + Offset: offset, + Hash: thisHash, } blocks = append(blocks, b) offset += n hf.Reset() - weakHf.Reset() } if len(blocks) == 0 { @@ -112,14 +99,8 @@ func Blocks(ctx context.Context, r io.Reader, blocksize int, sizehint int64, cou return blocks, nil } -// Validate quickly validates buf against the 32-bit weakHash, if not zero, -// else against the cryptohash hash, if len(hash)>0. It is satisfied if -// either hash matches or neither hash is given. -func Validate(buf, hash []byte, weakHash uint32) bool { - if weakHash != 0 && adler32.Checksum(buf) == weakHash { - return true - } - +// Validate validates the hash, if len(hash)>0. +func Validate(buf, hash []byte) bool { if len(hash) > 0 { hbuf := sha256.Sum256(buf) return bytes.Equal(hbuf[:], hash) diff --git a/lib/scanner/blocks_test.go b/lib/scanner/blocks_test.go index fd98e3ff2..b54eb4f74 100644 --- a/lib/scanner/blocks_test.go +++ b/lib/scanner/blocks_test.go @@ -9,51 +9,40 @@ package scanner import ( "bytes" "context" - "crypto/rand" "crypto/sha256" "fmt" - origAdler32 "hash/adler32" mrand "math/rand" "testing" - "testing/quick" - - rollingAdler32 "github.com/chmduquesne/rollinghash/adler32" - "github.com/syncthing/syncthing/lib/protocol" ) var blocksTestData = []struct { data []byte blocksize int hash []string - weakhash []uint32 }{ { []byte(""), 1024, []string{ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", }, - []uint32{0}, }, { []byte("contents"), 1024, []string{ "d1b2a59fbea7e20077af9f91b27e95e865061b270be03ff539ab3b73587882e8", }, - []uint32{0x0f3a036f}, }, { []byte("contents"), 9, []string{ "d1b2a59fbea7e20077af9f91b27e95e865061b270be03ff539ab3b73587882e8", }, - []uint32{0x0f3a036f}, }, { []byte("contents"), 8, []string{ "d1b2a59fbea7e20077af9f91b27e95e865061b270be03ff539ab3b73587882e8", }, - []uint32{0x0f3a036f}, }, { []byte("contents"), 7, @@ -61,7 +50,6 @@ var blocksTestData = []struct { "ed7002b439e9ac845f22357d822bac1444730fbdb6016d3ec9432297b9ec9f73", "043a718774c572bd8a25adbeb1bfcd5c0256ae11cecf9f9c3f925d0e52beaf89", }, - []uint32{0x0bcb02fc, 0x00740074}, }, { []byte("contents"), 3, @@ -70,7 +58,6 @@ var blocksTestData = []struct { "e4432baa90819aaef51d2a7f8e148bf7e679610f3173752fabb4dcb2d0f418d3", "44ad63f60af0f6db6fdde6d5186ef78176367df261fa06be3079b6c80c8adba4", }, - []uint32{0x02780141, 0x02970148, 0x015d00e8}, }, { []byte("conconts"), 3, @@ -79,7 +66,6 @@ var blocksTestData = []struct { "1143da2bc54c495c4be31d3868785d39ffdfd56df5668f0645d8f14d47647952", "44ad63f60af0f6db6fdde6d5186ef78176367df261fa06be3079b6c80c8adba4", }, - []uint32{0x02780141, 0x02780141, 0x015d00e8}, }, { []byte("contenten"), 3, @@ -88,14 +74,13 @@ var blocksTestData = []struct { "e4432baa90819aaef51d2a7f8e148bf7e679610f3173752fabb4dcb2d0f418d3", "e4432baa90819aaef51d2a7f8e148bf7e679610f3173752fabb4dcb2d0f418d3", }, - []uint32{0x02780141, 0x02970148, 0x02970148}, }, } func TestBlocks(t *testing.T) { for testNo, test := range blocksTestData { buf := bytes.NewBuffer(test.data) - blocks, err := Blocks(context.TODO(), buf, test.blocksize, -1, nil, true) + blocks, err := Blocks(context.TODO(), buf, test.blocksize, -1, nil) if err != nil { t.Fatal(err) } @@ -119,9 +104,6 @@ func TestBlocks(t *testing.T) { if h := fmt.Sprintf("%x", blocks[i].Hash); h != test.hash[i] { t.Errorf("%d/%d: Incorrect block hash %q != %q", testNo, i, h, test.hash[i]) } - if h := blocks[i].WeakHash; h != test.weakhash[i] { - t.Errorf("%d/%d: Incorrect block weakhash 0x%08x != 0x%08x", testNo, i, h, test.weakhash[i]) - } i++ } @@ -129,85 +111,10 @@ func TestBlocks(t *testing.T) { } } -func TestAdler32Variants(t *testing.T) { - // Verify that the two adler32 functions give matching results for a few - // different blocks of data. - - hf1 := origAdler32.New() - hf2 := rollingAdler32.New() - - checkFn := func(data []byte) bool { - hf1.Write(data) - sum1 := hf1.Sum32() - - hf2.Write(data) - sum2 := hf2.Sum32() - - hf1.Reset() - hf2.Reset() - - // Make sure whatever we use in Validate matches too resp. this - // tests gets adjusted if we ever switch the weak hash algo. - return sum1 == sum2 && Validate(data, nil, sum1) - } - - // protocol block sized data - data := make([]byte, protocol.MinBlockSize) - for i := 0; i < 5; i++ { - rand.Read(data) - if !checkFn(data) { - t.Errorf("Hash mismatch on block sized data") - } - } - - // random small blocks - if err := quick.Check(checkFn, nil); err != nil { - t.Error(err) - } - - // rolling should have the same result as the individual blocks - // themselves. - - windowSize := 128 - - hf3 := rollingAdler32.New() - hf3.Write(data[:windowSize]) - - for i := windowSize; i < len(data); i++ { - if i%windowSize == 0 { - // let the reference function catch up - window := data[i-windowSize : i] - hf1.Reset() - hf1.Write(window) - hf2.Reset() - hf2.Write(window) - - // verify that they are in sync with the rolling function - sum1 := hf1.Sum32() - sum2 := hf2.Sum32() - sum3 := hf3.Sum32() - t.Logf("At i=%d, sum2=%08x, sum3=%08x", i, sum2, sum3) - if sum2 != sum3 { - t.Errorf("Mismatch after roll; i=%d, sum2=%08x, sum3=%08x", i, sum2, sum3) - break - } - if sum1 != sum3 { - t.Errorf("Mismatch after roll; i=%d, sum1=%08x, sum3=%08x", i, sum1, sum3) - break - } - if !Validate(window, nil, sum1) { - t.Errorf("Validation failure after roll; i=%d", i) - } - } - hf3.Roll(data[i]) - } -} - func BenchmarkValidate(b *testing.B) { type block struct { - data []byte - hash [sha256.Size]byte - weakhash uint32 + data []byte + hash [sha256.Size]byte } var blocks []block const blocksPerType = 100 @@ -220,16 +127,6 @@ func BenchmarkValidate(b *testing.B) { b.data = make([]byte, 128<<10) r.Read(b.data) b.hash = sha256.Sum256(b.data) - b.weakhash = origAdler32.Checksum(b.data) - blocks = append(blocks, b) - } - // Blocks where the hash matches, but the weakhash doesn't. - for i := 0; i < blocksPerType; i++ { - var b block - b.data = make([]byte, 128<<10) - r.Read(b.data) - b.hash = sha256.Sum256(b.data) - b.weakhash = 1 // Zeros causes Validate to skip the weakhash. blocks = append(blocks, b) } @@ -238,7 +135,7 @@ func BenchmarkValidate(b *testing.B) { for i := 0; i < b.N; i++ { for _, b := range blocks { - Validate(b.data, b.hash[:], b.weakhash) + Validate(b.data, b.hash[:]) } } } diff --git a/lib/scanner/walk_test.go b/lib/scanner/walk_test.go index d924f2357..c644edaaa 100644 --- a/lib/scanner/walk_test.go +++ b/lib/scanner/walk_test.go @@ -163,7 +163,7 @@ func TestVerify(t *testing.T) { progress := newByteCounter() defer progress.Close() - blocks, err := Blocks(context.TODO(), buf, blocksize, -1, progress, false) + blocks, err := Blocks(context.TODO(), buf, blocksize, -1, progress) if err != nil { t.Fatal(err) } @@ -633,7 +633,7 @@ func BenchmarkHashFile(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - if _, err := HashFile(context.TODO(), "", testFs, testdataName, protocol.MinBlockSize, nil, true); err != nil { + if _, err := HashFile(context.TODO(), "", testFs, testdataName, protocol.MinBlockSize, nil); err != nil { b.Fatal(err) } } diff --git a/lib/stats/debug.go b/lib/stats/debug.go deleted file mode 100644 index 0681277df..000000000 --- a/lib/stats/debug.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (C) 2014 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package stats - -import ( - "github.com/syncthing/syncthing/lib/logger" -) - -var l = logger.DefaultLogger.NewFacility("stats", "Persistent device and folder statistics") diff --git a/lib/stats/device.go b/lib/stats/device.go index e6d97f683..d8d13a3da 100644 --- a/lib/stats/device.go +++ b/lib/stats/device.go @@ -9,9 +9,7 @@ package stats import ( "time" - "github.com/syncthing/syncthing/lib/db" - "github.com/syncthing/syncthing/lib/db/backend" - "github.com/syncthing/syncthing/lib/protocol" + "github.com/syncthing/syncthing/internal/db" ) const ( @@ -25,19 +23,17 @@ type DeviceStatistics struct { } type DeviceStatisticsReference struct { - ns *db.NamespacedKV - device protocol.DeviceID + kv *db.Typed } -func NewDeviceStatisticsReference(dba backend.Backend, device protocol.DeviceID) *DeviceStatisticsReference { +func NewDeviceStatisticsReference(kv *db.Typed) *DeviceStatisticsReference { return &DeviceStatisticsReference{ - ns: db.NewDeviceStatisticsNamespace(dba, device.String()), - device: device, + kv: kv, } } func (s *DeviceStatisticsReference) GetLastSeen() (time.Time, error) { - t, ok, err := s.ns.Time(lastSeenKey) + t, ok, err := s.kv.Time(lastSeenKey) if err != nil { return time.Time{}, err } else if !ok { @@ -45,29 +41,25 @@ func (s *DeviceStatisticsReference) GetLastSeen() (time.Time, error) { // time.Time{} from s.ns return time.Unix(0, 0), nil } - l.Debugln("stats.DeviceStatisticsReference.GetLastSeen:", s.device, t) return t, nil } func (s *DeviceStatisticsReference) GetLastConnectionDuration() (time.Duration, error) { - d, ok, err := s.ns.Int64(connDurationKey) + d, ok, err := s.kv.Int64(connDurationKey) if err != nil { return 0, err } else if !ok { return 0, nil } - l.Debugln("stats.DeviceStatisticsReference.GetLastConnectionDuration:", s.device, d) return time.Duration(d), nil } func (s *DeviceStatisticsReference) WasSeen() error { - l.Debugln("stats.DeviceStatisticsReference.WasSeen:", s.device) - return s.ns.PutTime(lastSeenKey, time.Now().Truncate(time.Second)) + return s.kv.PutTime(lastSeenKey, time.Now().Truncate(time.Second)) } func (s *DeviceStatisticsReference) LastConnectionDuration(d time.Duration) error { - l.Debugln("stats.DeviceStatisticsReference.LastConnectionDuration:", s.device, d) - return s.ns.PutInt64(connDurationKey, d.Nanoseconds()) + return s.kv.PutInt64(connDurationKey, d.Nanoseconds()) } func (s *DeviceStatisticsReference) GetStatistics() (DeviceStatistics, error) { diff --git a/lib/stats/folder.go b/lib/stats/folder.go index 8e67c6e84..a8d587359 100644 --- a/lib/stats/folder.go +++ b/lib/stats/folder.go @@ -9,7 +9,7 @@ package stats import ( "time" - "github.com/syncthing/syncthing/lib/db" + "github.com/syncthing/syncthing/internal/db" ) type FolderStatistics struct { @@ -18,8 +18,7 @@ type FolderStatistics struct { } type FolderStatisticsReference struct { - ns *db.NamespacedKV - folder string + kv *db.Typed } type LastFile struct { @@ -28,27 +27,26 @@ type LastFile struct { Deleted bool `json:"deleted"` } -func NewFolderStatisticsReference(ldb *db.Lowlevel, folder string) *FolderStatisticsReference { +func NewFolderStatisticsReference(kv *db.Typed) *FolderStatisticsReference { return &FolderStatisticsReference{ - ns: db.NewFolderStatisticsNamespace(ldb, folder), - folder: folder, + kv: kv, } } func (s *FolderStatisticsReference) GetLastFile() (LastFile, error) { - at, ok, err := s.ns.Time("lastFileAt") + at, ok, err := s.kv.Time("lastFileAt") if err != nil { return LastFile{}, err } else if !ok { return LastFile{}, nil } - file, ok, err := s.ns.String("lastFileName") + file, ok, err := s.kv.String("lastFileName") if err != nil { return LastFile{}, err } else if !ok { return LastFile{}, nil } - deleted, _, err := s.ns.Bool("lastFileDeleted") + deleted, _, err := s.kv.Bool("lastFileDeleted") if err != nil { return LastFile{}, err } @@ -60,25 +58,24 @@ func (s *FolderStatisticsReference) GetLastFile() (LastFile, error) { } func (s *FolderStatisticsReference) ReceivedFile(file string, deleted bool) error { - l.Debugln("stats.FolderStatisticsReference.ReceivedFile:", s.folder, file) - if err := s.ns.PutTime("lastFileAt", time.Now().Truncate(time.Second)); err != nil { + if err := s.kv.PutTime("lastFileAt", time.Now().Truncate(time.Second)); err != nil { return err } - if err := s.ns.PutString("lastFileName", file); err != nil { + if err := s.kv.PutString("lastFileName", file); err != nil { return err } - if err := s.ns.PutBool("lastFileDeleted", deleted); err != nil { + if err := s.kv.PutBool("lastFileDeleted", deleted); err != nil { return err } return nil } func (s *FolderStatisticsReference) ScanCompleted() error { - return s.ns.PutTime("lastScan", time.Now().Truncate(time.Second)) + return s.kv.PutTime("lastScan", time.Now().Truncate(time.Second)) } func (s *FolderStatisticsReference) GetLastScanTime() (time.Time, error) { - lastScan, ok, err := s.ns.Time("lastScan") + lastScan, ok, err := s.kv.Time("lastScan") if err != nil { return time.Time{}, err } else if !ok { diff --git a/lib/stats/stats_test.go b/lib/stats/stats_test.go index 0ebb42342..7f850ae36 100644 --- a/lib/stats/stats_test.go +++ b/lib/stats/stats_test.go @@ -13,15 +13,20 @@ import ( "testing" "time" - "github.com/syncthing/syncthing/lib/db/backend" - "github.com/syncthing/syncthing/lib/protocol" + "github.com/syncthing/syncthing/internal/db" + "github.com/syncthing/syncthing/internal/db/sqlite" ) func TestDeviceStat(t *testing.T) { - db := backend.OpenLevelDBMemory() - defer db.Close() + sdb, err := sqlite.OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + sdb.Close() + }) - sr := NewDeviceStatisticsReference(db, protocol.LocalDeviceID) + sr := NewDeviceStatisticsReference(db.NewTyped(sdb, "devstatref")) if err := sr.WasSeen(); err != nil { t.Fatal(err) } diff --git a/lib/syncthing/globalmigrations.go b/lib/syncthing/globalmigrations.go index cc4987497..2589f1882 100644 --- a/lib/syncthing/globalmigrations.go +++ b/lib/syncthing/globalmigrations.go @@ -7,13 +7,8 @@ package syncthing import ( - "encoding/binary" - "io" - + "github.com/syncthing/syncthing/internal/db" "github.com/syncthing/syncthing/lib/config" - "github.com/syncthing/syncthing/lib/db" - "github.com/syncthing/syncthing/lib/fs" - "github.com/syncthing/syncthing/lib/protocol" ) const ( @@ -21,8 +16,8 @@ const ( globalMigrationDBKey = "globalMigrationVersion" ) -func globalMigration(ll *db.Lowlevel, cfg config.Wrapper) error { - miscDB := db.NewMiscDataNamespace(ll) +func globalMigration(kv db.KV, cfg config.Wrapper) error { + miscDB := db.NewMiscDB(kv) prevVersion, _, err := miscDB.Int64(globalMigrationDBKey) if err != nil { return err @@ -32,101 +27,7 @@ func globalMigration(ll *db.Lowlevel, cfg config.Wrapper) error { return nil } - if prevVersion < 1 { - if err := encryptionTrailerSizeMigration(ll, cfg); err != nil { - return err - } - } + // currently no migrations return miscDB.PutInt64(globalMigrationDBKey, globalMigrationVersion) } - -func encryptionTrailerSizeMigration(ll *db.Lowlevel, cfg config.Wrapper) error { - encFolders := cfg.Folders() - for folderID, folderCfg := range cfg.Folders() { - if folderCfg.Type != config.FolderTypeReceiveEncrypted { - delete(encFolders, folderID) - } - } - if len(encFolders) == 0 { - return nil - } - - l.Infoln("Running global migration to fix encryption file sizes") - - // Trigger index re-transfer with fixed up sizes - db.DropDeltaIndexIDs(ll) - - for folderID, folderCfg := range encFolders { - fset, err := db.NewFileSet(folderID, ll) - if err != nil { - return err - } - snap, err := fset.Snapshot() - if err != nil { - return err - } - batch := db.NewFileInfoBatch(func(files []protocol.FileInfo) error { - // As we can't touch the version, we need to first invalidate the - // files, and then re-add the modified valid files - invalidFiles := make([]protocol.FileInfo, len(files)) - for i, f := range files { - f.SetUnsupported() - invalidFiles[i] = f - } - fset.Update(protocol.LocalDeviceID, invalidFiles) - fset.Update(protocol.LocalDeviceID, files) - return nil - }) - filesystem := folderCfg.Filesystem(fset) - var innerErr error - snap.WithHave(protocol.LocalDeviceID, func(fi protocol.FileInfo) bool { - size, err := sizeOfEncryptedTrailer(filesystem, fi.Name) - if err != nil { - // Best effort: If we fail to read a file, it will show as - // locally changed on next scan. - return true - } - fi.EncryptionTrailerSize = size - batch.Append(fi) - err = batch.FlushIfFull() - if err != nil { - innerErr = err - return false - } - return true - }) - snap.Release() - if innerErr != nil { - return innerErr - } - err = batch.Flush() - if err != nil { - return err - } - } - - return nil -} - -// sizeOfEncryptedTrailer returns the size of the encrypted trailer on disk. -// This amount of bytes should be subtracted from the file size to get the -// original file size. -func sizeOfEncryptedTrailer(fs fs.Filesystem, name string) (int, error) { - f, err := fs.Open(name) - if err != nil { - return 0, err - } - defer f.Close() - if _, err := f.Seek(-4, io.SeekEnd); err != nil { - return 0, err - } - var buf [4]byte - if _, err := io.ReadFull(f, buf[:]); err != nil { - return 0, err - } - // The stored size is the size of the encrypted data. - size := int(binary.BigEndian.Uint32(buf[:])) - // We add the size of the length word itself as well. - return size + 4, nil -} diff --git a/lib/syncthing/internals.go b/lib/syncthing/internals.go index 704cfba2a..af82b9ac4 100644 --- a/lib/syncthing/internals.go +++ b/lib/syncthing/internals.go @@ -8,9 +8,10 @@ package syncthing import ( "context" + "iter" "time" - "github.com/syncthing/syncthing/lib/db" + "github.com/syncthing/syncthing/internal/db" "github.com/syncthing/syncthing/lib/model" "github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/stats" @@ -23,6 +24,8 @@ type Internals struct { model model.Model } +type Counts = db.Counts + func newInternals(model model.Model) *Internals { return &Internals{ model: model, @@ -42,7 +45,7 @@ func (m *Internals) SetIgnores(folderID string, content []string) error { } func (m *Internals) DownloadBlock(ctx context.Context, deviceID protocol.DeviceID, folderID string, path string, blockNumber int, blockInfo protocol.BlockInfo, allowFromTemporary bool) ([]byte, error) { - return m.model.RequestGlobal(ctx, deviceID, folderID, path, int(blockNumber), blockInfo.Offset, blockInfo.Size, blockInfo.Hash, blockInfo.WeakHash, allowFromTemporary) + return m.model.RequestGlobal(ctx, deviceID, folderID, path, int(blockNumber), blockInfo.Offset, blockInfo.Size, blockInfo.Hash, allowFromTemporary) } func (m *Internals) BlockAvailability(folderID string, file protocol.FileInfo, block protocol.BlockInfo) ([]model.Availability, error) { @@ -77,14 +80,38 @@ func (m *Internals) PendingFolders(deviceID protocol.DeviceID) (map[string]db.Pe return m.model.PendingFolders(deviceID) } -func (m *Internals) DBSnapshot(folderID string) (*db.Snapshot, error) { - return m.model.DBSnapshot(folderID) -} - func (m *Internals) ScanFolderSubdirs(folderID string, paths []string) error { return m.model.ScanFolderSubdirs(folderID, paths) } +func (m *Internals) GlobalSize(folder string) (Counts, error) { + counts, err := m.model.GlobalSize(folder) + if err != nil { + return Counts{}, err + } + return counts, nil +} + +func (m *Internals) LocalSize(folder string) (Counts, error) { + counts, err := m.model.LocalSize(folder, protocol.LocalDeviceID) + if err != nil { + return Counts{}, err + } + return counts, nil +} + +func (m *Internals) NeedSize(folder string, device protocol.DeviceID) (Counts, error) { + counts, err := m.model.NeedSize(folder, device) + if err != nil { + return Counts{}, err + } + return counts, nil +} + +func (m *Internals) AllGlobalFiles(folder string) (iter.Seq[db.FileMetadata], func() error) { + return m.model.AllGlobalFiles(folder) +} + func (m *Internals) FolderProgressBytesCompleted(folder string) int64 { return m.model.FolderProgressBytesCompleted(folder) } diff --git a/lib/syncthing/syncthing.go b/lib/syncthing/syncthing.go index 733a095e7..019aeb47f 100644 --- a/lib/syncthing/syncthing.go +++ b/lib/syncthing/syncthing.go @@ -22,13 +22,12 @@ import ( "github.com/thejerf/suture/v4" + "github.com/syncthing/syncthing/internal/db" "github.com/syncthing/syncthing/lib/api" "github.com/syncthing/syncthing/lib/build" "github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/connections" "github.com/syncthing/syncthing/lib/connections/registry" - "github.com/syncthing/syncthing/lib/db" - "github.com/syncthing/syncthing/lib/db/backend" "github.com/syncthing/syncthing/lib/discover" "github.com/syncthing/syncthing/lib/events" "github.com/syncthing/syncthing/lib/locations" @@ -52,21 +51,19 @@ const ( ) type Options struct { - AuditWriter io.Writer - NoUpgrade bool - ProfilerAddr string - ResetDeltaIdxs bool - Verbose bool - // null duration means use default value - DBRecheckInterval time.Duration - DBIndirectGCInterval time.Duration + AuditWriter io.Writer + NoUpgrade bool + ProfilerAddr string + ResetDeltaIdxs bool + Verbose bool + DBMaintenanceInterval time.Duration } type App struct { myID protocol.DeviceID mainService *suture.Supervisor cfg config.Wrapper - ll *db.Lowlevel + sdb db.DB evLogger events.Logger cert tls.Certificate opts Options @@ -80,14 +77,10 @@ type App struct { Internals *Internals } -func New(cfg config.Wrapper, dbBackend backend.Backend, evLogger events.Logger, cert tls.Certificate, opts Options) (*App, error) { - ll, err := db.NewLowlevel(dbBackend, evLogger, db.WithRecheckInterval(opts.DBRecheckInterval), db.WithIndirectGCInterval(opts.DBIndirectGCInterval)) - if err != nil { - return nil, err - } +func New(cfg config.Wrapper, sdb db.DB, evLogger events.Logger, cert tls.Certificate, opts Options) (*App, error) { a := &App{ cfg: cfg, - ll: ll, + sdb: sdb, evLogger: evLogger, opts: opts, cert: cert, @@ -124,7 +117,7 @@ func (a *App) Start() error { func (a *App) startup() error { a.mainService.Add(ur.NewFailureHandler(a.cfg, a.evLogger)) - a.mainService.Add(a.ll) + a.mainService.Add(a.sdb.Service(a.opts.DBMaintenanceInterval)) if a.opts.AuditWriter != nil { a.mainService.Add(newAuditService(a.opts.AuditWriter, a.evLogger)) @@ -177,17 +170,15 @@ func (a *App) startup() error { }() } - perf := ur.CpuBench(context.Background(), 3, 150*time.Millisecond, true) + perf := ur.CpuBench(context.Background(), 3, 150*time.Millisecond) l.Infof("Hashing performance is %.02f MB/s", perf) - if err := db.UpdateSchema(a.ll); err != nil { - l.Warnln("Database schema:", err) - return err - } - if a.opts.ResetDeltaIdxs { l.Infoln("Reinitializing delta index IDs") - db.DropDeltaIndexIDs(a.ll) + if err := a.sdb.DropAllIndexIDs(); err != nil { + l.Warnln("Drop index IDs:", err) + return err + } } protectedFiles := []string{ @@ -198,17 +189,22 @@ func (a *App) startup() error { } // Remove database entries for folders that no longer exist in the config - folders := a.cfg.Folders() - for _, folder := range a.ll.ListFolders() { - if _, ok := folders[folder]; !ok { + cfgFolders := a.cfg.Folders() + dbFolders, err := a.sdb.ListFolders() + if err != nil { + l.Warnln("Listing folders:", err) + return err + } + for _, folder := range dbFolders { + if _, ok := cfgFolders[folder]; !ok { l.Infof("Cleaning metadata for dropped folder %q", folder) - db.DropFolder(a.ll, folder) + a.sdb.DropFolder(folder) } } // Grab the previously running version string from the database. - miscDB := db.NewMiscDataNamespace(a.ll) + miscDB := db.NewMiscDB(a.sdb) prevVersion, _, err := miscDB.String("prevVersion") if err != nil { l.Warnln("Database:", err) @@ -229,7 +225,10 @@ func (a *App) startup() error { if a.cfg.Options().SendFullIndexOnUpgrade { // Drop delta indexes in case we've changed random stuff we // shouldn't have. We will resend our index on next connect. - db.DropDeltaIndexIDs(a.ll) + if err := a.sdb.DropAllIndexIDs(); err != nil { + l.Warnln("Drop index IDs:", err) + return err + } } } @@ -238,13 +237,13 @@ func (a *App) startup() error { miscDB.PutString("prevVersion", build.Version) } - if err := globalMigration(a.ll, a.cfg); err != nil { + if err := globalMigration(a.sdb, a.cfg); err != nil { l.Warnln("Global migration:", err) return err } keyGen := protocol.NewKeyGenerator() - m := model.NewModel(a.cfg, a.myID, a.ll, protectedFiles, a.evLogger, keyGen) + m := model.NewModel(a.cfg, a.myID, a.sdb, protectedFiles, a.evLogger, keyGen) a.Internals = newInternals(m) a.mainService.Add(m) @@ -327,7 +326,7 @@ func (a *App) wait(errChan <-chan error) { done := make(chan struct{}) go func() { - a.ll.Close() + a.sdb.Close() close(done) }() select { @@ -393,7 +392,7 @@ func (a *App) stopWithErr(stopReason svcutil.ExitStatus, err error) svcutil.Exit return a.exitStatus } -func (a *App) setupGUI(m model.Model, defaultSub, diskSub events.BufferedSubscription, discoverer discover.Manager, connectionsService connections.Service, urService *ur.Service, errors, systemLog logger.Recorder, miscDB *db.NamespacedKV) error { +func (a *App) setupGUI(m model.Model, defaultSub, diskSub events.BufferedSubscription, discoverer discover.Manager, connectionsService connections.Service, urService *ur.Service, errors, systemLog logger.Recorder, miscDB *db.Typed) error { guiCfg := a.cfg.GUI() if !guiCfg.Enabled { diff --git a/lib/syncthing/syncthing_test.go b/lib/syncthing/syncthing_test.go index e9feedf44..89dcabc1c 100644 --- a/lib/syncthing/syncthing_test.go +++ b/lib/syncthing/syncthing_test.go @@ -8,11 +8,12 @@ package syncthing import ( "os" + "strings" "testing" "time" + "github.com/syncthing/syncthing/internal/db/sqlite" "github.com/syncthing/syncthing/lib/config" - "github.com/syncthing/syncthing/lib/db/backend" "github.com/syncthing/syncthing/lib/events" "github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/svcutil" @@ -71,8 +72,14 @@ func TestStartupFail(t *testing.T) { }, protocol.LocalDeviceID, events.NoopLogger) defer os.Remove(cfg.ConfigPath()) - db := backend.OpenMemory() - app, err := New(cfg, db, events.NoopLogger, cert, Options{}) + sdb, err := sqlite.OpenTemp() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + sdb.Close() + }) + app, err := New(cfg, sdb, events.NoopLogger, cert, Options{}) if err != nil { t.Fatal(err) } @@ -102,10 +109,9 @@ func TestStartupFail(t *testing.T) { t.Errorf(`Got different errors "%v" from Start and "%v" from Error`, startErr, err) } - if trans, err := db.NewReadTransaction(); err == nil { + if _, err := sdb.ListFolders(); err == nil { t.Error("Expected error due to db being closed, got nil") - trans.Release() - } else if !backend.IsClosed(err) { + } else if !strings.Contains(err.Error(), "closed") { t.Error("Expected error due to db being closed, got", err) } } diff --git a/lib/syncthing/utils.go b/lib/syncthing/utils.go index 023bbd8f1..79d2cd6b3 100644 --- a/lib/syncthing/utils.go +++ b/lib/syncthing/utils.go @@ -12,9 +12,15 @@ import ( "fmt" "io" "os" + "sync" + "time" + "github.com/syncthing/syncthing/internal/db" + "github.com/syncthing/syncthing/internal/db/olddb" + "github.com/syncthing/syncthing/internal/db/olddb/backend" + "github.com/syncthing/syncthing/internal/db/sqlite" + "github.com/syncthing/syncthing/lib/build" "github.com/syncthing/syncthing/lib/config" - "github.com/syncthing/syncthing/lib/db/backend" "github.com/syncthing/syncthing/lib/events" "github.com/syncthing/syncthing/lib/fs" "github.com/syncthing/syncthing/lib/locations" @@ -58,7 +64,7 @@ func GenerateCertificate(certFile, keyFile string) (tls.Certificate, error) { return tlsutil.NewCertificate(certFile, keyFile, tlsDefaultCommonName, deviceCertLifetimeDays) } -func DefaultConfig(path string, myID protocol.DeviceID, evLogger events.Logger, noDefaultFolder, skipPortProbing bool) (config.Wrapper, error) { +func DefaultConfig(path string, myID protocol.DeviceID, evLogger events.Logger, skipPortProbing bool) (config.Wrapper, error) { newCfg := config.New(myID) if skipPortProbing { @@ -69,30 +75,18 @@ func DefaultConfig(path string, myID protocol.DeviceID, evLogger events.Logger, return nil, err } - if noDefaultFolder { - l.Infoln("We will skip creation of a default folder on first start") - return config.Wrap(path, newCfg, myID, evLogger), nil - } - - fcfg := newCfg.Defaults.Folder.Copy() - fcfg.ID = "default" - fcfg.Label = "Default Folder" - fcfg.FilesystemType = config.FilesystemTypeBasic - fcfg.Path = locations.Get(locations.DefFolder) - newCfg.Folders = append(newCfg.Folders, fcfg) - l.Infoln("Default folder created and/or linked to new config") return config.Wrap(path, newCfg, myID, evLogger), nil } // LoadConfigAtStartup loads an existing config. If it doesn't yet exist, it -// creates a default one, without the default folder if noDefaultFolder is true. -// Otherwise it checks the version, and archives and upgrades the config if -// necessary or returns an error, if the version isn't compatible. -func LoadConfigAtStartup(path string, cert tls.Certificate, evLogger events.Logger, allowNewerConfig, noDefaultFolder, skipPortProbing bool) (config.Wrapper, error) { +// creates a default one. Otherwise it checks the version, and archives and +// upgrades the config if necessary or returns an error, if the version +// isn't compatible. +func LoadConfigAtStartup(path string, cert tls.Certificate, evLogger events.Logger, allowNewerConfig, skipPortProbing bool) (config.Wrapper, error) { myID := protocol.NewDeviceID(cert.Certificate[0]) cfg, originalVersion, err := config.Load(path, myID, evLogger) if fs.IsNotExist(err) { - cfg, err = DefaultConfig(path, myID, evLogger, noDefaultFolder, skipPortProbing) + cfg, err = DefaultConfig(path, myID, evLogger, skipPortProbing) if err != nil { return nil, fmt.Errorf("failed to generate default config: %w", err) } @@ -150,6 +144,131 @@ func copyFile(src, dst string) error { return nil } -func OpenDBBackend(path string, tuning config.Tuning) (backend.Backend, error) { - return backend.Open(path, backend.Tuning(tuning)) +// Opens a database +func OpenDatabase(path string, deleteRetention time.Duration) (db.DB, error) { + sql, err := sqlite.Open(path, sqlite.WithDeleteRetention(deleteRetention)) + if err != nil { + return nil, err + } + + sdb := db.MetricsWrap(sql) + + return sdb, nil +} + +// Attempts migration of the old (LevelDB-based) database type to the new (SQLite-based) type +func TryMigrateDatabase(deleteRetention time.Duration) error { + oldDBDir := locations.Get(locations.LegacyDatabase) + if _, err := os.Lstat(oldDBDir); err != nil { + // No old database + return nil + } + + be, err := backend.OpenLevelDBRO(oldDBDir) + if err != nil { + // Apparently, not a valid old database + return nil + } + defer be.Close() + + sdb, err := sqlite.OpenForMigration(locations.Get(locations.Database)) + if err != nil { + return err + } + defer sdb.Close() + + miscDB := db.NewMiscDB(sdb) + if when, ok, err := miscDB.Time("migrated-from-leveldb-at"); err == nil && ok { + l.Warnf("Old-style database present but already migrated at %v; please manually move or remove %s.", when, oldDBDir) + return nil + } + + l.Infoln("Migrating old-style database to SQLite; this may take a while...") + t0 := time.Now() + + ll, err := olddb.NewLowlevel(be) + if err != nil { + return err + } + + totFiles, totBlocks := 0, 0 + for _, folder := range ll.ListFolders() { + // Start a writer routine + fis := make(chan protocol.FileInfo, 50) + var writeErr error + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + var batch []protocol.FileInfo + files, blocks := 0, 0 + t0 := time.Now() + t1 := time.Now() + for fi := range fis { + batch = append(batch, fi) + files++ + blocks += len(fi.Blocks) + if len(batch) == 1000 { + writeErr = sdb.Update(folder, protocol.LocalDeviceID, batch) + if writeErr != nil { + return + } + batch = batch[:0] + if time.Since(t1) > 10*time.Second { + d := time.Since(t0) + 1 + t1 = time.Now() + l.Infof("Migrating folder %s... (%d files and %dk blocks in %v, %.01f files/s)", folder, files, blocks/1000, d.Truncate(time.Second), float64(files)/d.Seconds()) + } + } + } + if len(batch) > 0 { + writeErr = sdb.Update(folder, protocol.LocalDeviceID, batch) + } + d := time.Since(t0) + 1 + l.Infof("Migrated folder %s; %d files and %dk blocks in %v, %.01f files/s", folder, files, blocks/1000, d.Truncate(time.Second), float64(files)/d.Seconds()) + totFiles += files + totBlocks += blocks + }() + + // Iterate the existing files + fs, err := olddb.NewFileSet(folder, ll) + if err != nil { + return err + } + snap, err := fs.Snapshot() + if err != nil { + return err + } + _ = snap.WithHaveSequence(0, func(fi protocol.FileInfo) bool { + if deleteRetention > 0 && fi.Deleted && time.Since(fi.ModTime()) > deleteRetention { + // Skip deleted files that match the garbage collection + // criteria in the database + return true + } + fis <- fi + return true + }) + close(fis) + snap.Release() + + // Wait for writes to complete + wg.Wait() + if writeErr != nil { + return writeErr + } + } + + l.Infoln("Migrating virtual mtimes...") + if err := ll.IterateMtimes(sdb.PutMtime); err != nil { + l.Warnln("Failed to migrate mtimes:", err) + } + + _ = miscDB.PutTime("migrated-from-leveldb-at", time.Now()) + _ = miscDB.PutString("migrated-from-leveldb-by", build.LongVersion) + + _ = be.Close() + _ = os.Rename(oldDBDir, oldDBDir+"-migrated") + + l.Infof("Migration complete, %d files and %dk blocks in %s", totFiles, totBlocks/1000, time.Since(t0).Truncate(time.Second)) + return nil } diff --git a/lib/ur/contract/contract.go b/lib/ur/contract/contract.go index e78dc4112..a55acbd5f 100644 --- a/lib/ur/contract/contract.go +++ b/lib/ur/contract/contract.go @@ -111,8 +111,6 @@ type Report struct { ConflictsOther int `json:"conflictsOther,omitempty" metric:"folder_feature{feature=ConflictsOther},summary" since:"3"` DisableSparseFiles int `json:"disableSparseFiles,omitempty" metric:"folder_feature{feature=DisableSparseFiles},summary" since:"3"` DisableTempIndexes int `json:"disableTempIndexes,omitempty" metric:"folder_feature{feature=DisableTempIndexes},summary" since:"3"` - AlwaysWeakHash int `json:"alwaysWeakHash,omitempty" metric:"folder_feature{feature=AlwaysWeakhash},summary" since:"3"` - CustomWeakHashThreshold int `json:"customWeakHashThreshold,omitempty" metric:"folder_feature{feature=CustomWeakhashThreshold},summary" since:"3"` FsWatcherEnabled int `json:"fsWatcherEnabled,omitempty" metric:"folder_feature{feature=FSWatcherEnabled},summary" since:"3"` PullOrder map[string]int `json:"pullOrder,omitempty" metric:"folder_pull_order,summaryVec:order" since:"3"` FilesystemType map[string]int `json:"filesystemType,omitempty" metric:"folder_file_system_type,summaryVec:type" since:"3"` @@ -152,13 +150,12 @@ type Report struct { } `json:"guiStats,omitempty" since:"3"` BlockStats struct { - Total int `json:"total,omitempty" metric:"blocks_processed_total,gauge" since:"3"` - Renamed int `json:"renamed,omitempty" metric:"blocks_processed{source=renamed},gauge" since:"3"` - Reused int `json:"reused,omitempty" metric:"blocks_processed{source=reused},gauge" since:"3"` - Pulled int `json:"pulled,omitempty" metric:"blocks_processed{source=pulled},gauge" since:"3"` - CopyOrigin int `json:"copyOrigin,omitempty" metric:"blocks_processed{source=copy_origin},gauge" since:"3"` - CopyOriginShifted int `json:"copyOriginShifted,omitempty" metric:"blocks_processed{source=copy_origin_shifted},gauge" since:"3"` - CopyElsewhere int `json:"copyElsewhere,omitempty" metric:"blocks_processed{source=copy_elsewhere},gauge" since:"3"` + Total int `json:"total,omitempty" metric:"blocks_processed_total,gauge" since:"3"` + Renamed int `json:"renamed,omitempty" metric:"blocks_processed{source=renamed},gauge" since:"3"` + Reused int `json:"reused,omitempty" metric:"blocks_processed{source=reused},gauge" since:"3"` + Pulled int `json:"pulled,omitempty" metric:"blocks_processed{source=pulled},gauge" since:"3"` + CopyOrigin int `json:"copyOrigin,omitempty" metric:"blocks_processed{source=copy_origin},gauge" since:"3"` + CopyElsewhere int `json:"copyElsewhere,omitempty" metric:"blocks_processed{source=copy_elsewhere},gauge" since:"3"` } `json:"blockStats,omitempty" since:"3"` TransportStats map[string]int `json:"transportStats,omitempty" since:"3"` @@ -175,9 +172,6 @@ type Report struct { Stars int `json:"stars,omitempty" metric:"folder_ignore_lines{kind=stars},summary" since:"3"` } `json:"ignoreStats,omitempty" since:"3"` - // V3 fields added late in the RC - WeakHashEnabled bool `json:"weakHashEnabled,omitempty" metric:"-" since:"3"` // Deprecated and not provided client-side anymore - // Added in post processing Received time.Time `json:"received,omitempty"` Date string `json:"date,omitempty"` diff --git a/lib/ur/usage_report.go b/lib/ur/usage_report.go index c80168738..b8dbb4e6a 100644 --- a/lib/ur/usage_report.go +++ b/lib/ur/usage_report.go @@ -22,10 +22,10 @@ import ( "time" "github.com/shirou/gopsutil/v4/process" + "github.com/syncthing/syncthing/internal/db" "github.com/syncthing/syncthing/lib/build" "github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/connections" - "github.com/syncthing/syncthing/lib/db" "github.com/syncthing/syncthing/lib/dialer" "github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/scanner" @@ -41,7 +41,7 @@ const Version = 3 var StartTime = time.Now().Truncate(time.Second) type Model interface { - DBSnapshot(folder string) (*db.Snapshot, error) + GlobalSize(folder string) (db.Counts, error) UsageReportingStats(report *contract.Report, version int, preview bool) } @@ -83,12 +83,10 @@ func (s *Service) reportData(ctx context.Context, urVersion int, preview bool) ( var totFiles, maxFiles int var totBytes, maxBytes int64 for folderID := range s.cfg.Folders() { - snap, err := s.model.DBSnapshot(folderID) + global, err := s.model.GlobalSize(folderID) if err != nil { - continue + return nil, err } - global := snap.GlobalSize() - snap.Release() totFiles += int(global.Files) totBytes += global.Bytes if int(global.Files) > maxFiles { @@ -116,8 +114,8 @@ func (s *Service) reportData(ctx context.Context, urVersion int, preview bool) ( report.TotMiB = int(totBytes / 1024 / 1024) report.FolderMaxMiB = int(maxBytes / 1024 / 1024) report.MemoryUsageMiB = int((mem.Sys - mem.HeapReleased) / 1024 / 1024) - report.SHA256Perf = CpuBench(ctx, 5, 125*time.Millisecond, false) - report.HashPerf = CpuBench(ctx, 5, 125*time.Millisecond, true) + report.SHA256Perf = CpuBench(ctx, 5, 125*time.Millisecond) + report.HashPerf = report.SHA256Perf report.MemorySize = int(memorySize() / 1024 / 1024) report.NumCPU = runtime.NumCPU() @@ -252,11 +250,6 @@ func (s *Service) reportData(ctx context.Context, urVersion int, preview bool) ( if cfg.DisableTempIndexes { report.FolderUsesV3.DisableTempIndexes++ } - if cfg.WeakHashThresholdPct < 0 { - report.FolderUsesV3.AlwaysWeakHash++ - } else if cfg.WeakHashThresholdPct != 25 { - report.FolderUsesV3.CustomWeakHashThreshold++ - } if cfg.FSWatcherEnabled { report.FolderUsesV3.FsWatcherEnabled++ } @@ -443,7 +436,7 @@ var ( ) // CpuBench returns CPU performance as a measure of single threaded SHA-256 MiB/s -func CpuBench(ctx context.Context, iterations int, duration time.Duration, useWeakHash bool) float64 { +func CpuBench(ctx context.Context, iterations int, duration time.Duration) float64 { blocksResultMut.Lock() defer blocksResultMut.Unlock() @@ -454,7 +447,7 @@ func CpuBench(ctx context.Context, iterations int, duration time.Duration, useWe var perf float64 for i := 0; i < iterations; i++ { - if v := cpuBenchOnce(ctx, duration, useWeakHash, bs); v > perf { + if v := cpuBenchOnce(ctx, duration, bs); v > perf { perf = v } } @@ -467,13 +460,13 @@ func CpuBench(ctx context.Context, iterations int, duration time.Duration, useWe return perf } -func cpuBenchOnce(ctx context.Context, duration time.Duration, useWeakHash bool, bs []byte) float64 { +func cpuBenchOnce(ctx context.Context, duration time.Duration, bs []byte) float64 { t0 := time.Now() b := 0 var err error for time.Since(t0) < duration { r := bytes.NewReader(bs) - blocksResult, err = scanner.Blocks(ctx, r, protocol.MinBlockSize, int64(len(bs)), nil, useWeakHash) + blocksResult, err = scanner.Blocks(ctx, r, protocol.MinBlockSize, int64(len(bs)), nil) if err != nil { return 0 // Context done } diff --git a/lib/versioner/external.go b/lib/versioner/external.go index 656bdfa72..e5a29574f 100644 --- a/lib/versioner/external.go +++ b/lib/versioner/external.go @@ -41,7 +41,7 @@ func newExternal(cfg config.FolderConfiguration) Versioner { s := external{ command: command, - filesystem: cfg.Filesystem(nil), + filesystem: cfg.Filesystem(), } l.Debugf("instantiated %#v", s) diff --git a/lib/versioner/simple.go b/lib/versioner/simple.go index 24b861346..25ec04a15 100644 --- a/lib/versioner/simple.go +++ b/lib/versioner/simple.go @@ -41,7 +41,7 @@ func newSimple(cfg config.FolderConfiguration) Versioner { s := simple{ keep: keep, cleanoutDays: cleanoutDays, - folderFs: cfg.Filesystem(nil), + folderFs: cfg.Filesystem(), versionsFs: versionerFsFromFolderCfg(cfg), copyRangeMethod: cfg.CopyRangeMethod.ToFS(), } diff --git a/lib/versioner/simple_test.go b/lib/versioner/simple_test.go index 0bdbeeaff..d2d008706 100644 --- a/lib/versioner/simple_test.go +++ b/lib/versioner/simple_test.go @@ -65,7 +65,7 @@ func TestSimpleVersioningVersionCount(t *testing.T) { }, }, } - fs := cfg.Filesystem(nil) + fs := cfg.Filesystem() v := newSimple(cfg) @@ -117,7 +117,7 @@ func TestPathTildes(t *testing.T) { }, }, } - fs := cfg.Filesystem(nil) + fs := cfg.Filesystem() v := newSimple(cfg) const testPath = "test" @@ -177,7 +177,7 @@ func TestArchiveFoldersCreationPermission(t *testing.T) { }, }, } - vfs := cfg.Filesystem(nil) + vfs := cfg.Filesystem() v := newSimple(cfg) // Create two folders and set their permissions diff --git a/lib/versioner/staggered.go b/lib/versioner/staggered.go index 9bb56488a..310d3d141 100644 --- a/lib/versioner/staggered.go +++ b/lib/versioner/staggered.go @@ -44,7 +44,7 @@ func newStaggered(cfg config.FolderConfiguration) Versioner { versionsFs := versionerFsFromFolderCfg(cfg) s := &staggered{ - folderFs: cfg.Filesystem(nil), + folderFs: cfg.Filesystem(), versionsFs: versionsFs, interval: [4]interval{ {30, 60 * 60}, // first hour -> 30 sec between versions diff --git a/lib/versioner/trashcan.go b/lib/versioner/trashcan.go index 7378c3a33..2e228900d 100644 --- a/lib/versioner/trashcan.go +++ b/lib/versioner/trashcan.go @@ -33,7 +33,7 @@ func newTrashcan(cfg config.FolderConfiguration) Versioner { // On error we default to 0, "do not clean out the trash can" s := &trashcan{ - folderFs: cfg.Filesystem(nil), + folderFs: cfg.Filesystem(), versionsFs: versionerFsFromFolderCfg(cfg), cleanoutDays: cleanoutDays, copyRangeMethod: cfg.CopyRangeMethod.ToFS(), diff --git a/lib/versioner/trashcan_test.go b/lib/versioner/trashcan_test.go index a12ee6076..d1abe0349 100644 --- a/lib/versioner/trashcan_test.go +++ b/lib/versioner/trashcan_test.go @@ -34,7 +34,7 @@ func TestTrashcanArchiveRestoreSwitcharoo(t *testing.T) { FSPath: tmpDir2, }, } - folderFs := cfg.Filesystem(nil) + folderFs := cfg.Filesystem() versionsFs := fs.NewFilesystem(fs.FilesystemTypeBasic, tmpDir2) @@ -113,7 +113,7 @@ func TestTrashcanRestoreDeletedFile(t *testing.T) { }, } - folderFs := cfg.Filesystem(nil) + folderFs := cfg.Filesystem() versionsFs := fs.NewFilesystem(fs.FilesystemTypeBasic, tmpDir2) @@ -209,7 +209,7 @@ func TestTrashcanCleanOut(t *testing.T) { }, } - fs := cfg.Filesystem(nil) + fs := cfg.Filesystem() v := newTrashcan(cfg) diff --git a/lib/versioner/util.go b/lib/versioner/util.go index 070cdf5bb..2286f762c 100644 --- a/lib/versioner/util.go +++ b/lib/versioner/util.go @@ -303,7 +303,7 @@ func restoreFile(method fs.CopyRangeMethod, src, dst fs.Filesystem, filePath str } func versionerFsFromFolderCfg(cfg config.FolderConfiguration) (versionsFs fs.Filesystem) { - folderFs := cfg.Filesystem(nil) + folderFs := cfg.Filesystem() if cfg.Versioning.FSPath == "" { versionsFs = fs.NewFilesystem(folderFs.Type(), filepath.Join(folderFs.URI(), DefaultPath)) } else if cfg.Versioning.FSType == config.FilesystemTypeBasic { diff --git a/lib/weakhash/benchmark_test.go b/lib/weakhash/benchmark_test.go deleted file mode 100644 index bd30e8bbf..000000000 --- a/lib/weakhash/benchmark_test.go +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright (C) 2016 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package weakhash - -import ( - "bytes" - "context" - "fmt" - "hash" - vadler32 "hash/adler32" - "io" - "math/rand" - "os" - "testing" - - "github.com/chmduquesne/rollinghash/adler32" - "github.com/chmduquesne/rollinghash/bozo32" - "github.com/chmduquesne/rollinghash/buzhash32" - "github.com/chmduquesne/rollinghash/buzhash64" -) - -const ( - testFile = "../model/testdata/tmpfile" - size = 128 << 10 -) - -func BenchmarkFind1MFile(b *testing.B) { - b.ReportAllocs() - b.SetBytes(1 << 20) - for i := 0; i < b.N; i++ { - fd, err := os.Open(testFile) - if err != nil { - b.Fatal(err) - } - _, err = Find(context.Background(), fd, []uint32{0, 1, 2}, size) - if err != nil { - b.Fatal(err) - } - fd.Close() - } -} - -type RollingHash interface { - hash.Hash - Roll(byte) -} - -func BenchmarkBlock(b *testing.B) { - tests := []struct { - name string - hash hash.Hash - }{ - { - "adler32", adler32.New(), - }, - { - "bozo32", bozo32.New(), - }, - { - "buzhash32", buzhash32.New(), - }, - { - "buzhash64", buzhash64.New(), - }, - { - "vanilla-adler32", vadler32.New(), - }, - } - - sizes := []int64{128 << 10, 16 << 20} - - buf := make([]byte, 16<<20) - rand.Read(buf) - - for _, testSize := range sizes { - for _, test := range tests { - b.Run(test.name+"-"+fmt.Sprint(testSize), func(bb *testing.B) { - bb.Run("", func(bbb *testing.B) { - bbb.ResetTimer() - for i := 0; i < bbb.N; i++ { - lr := io.LimitReader(bytes.NewReader(buf), testSize) - n, err := io.Copy(test.hash, lr) - if err != nil { - bbb.Error(err) - } - if n != testSize { - bbb.Errorf("%d != %d", n, testSize) - } - - test.hash.Sum(nil) - test.hash.Reset() - } - - bbb.SetBytes(testSize) - bbb.ReportAllocs() - }) - }) - } - } -} - -func BenchmarkRoll(b *testing.B) { - tests := []struct { - name string - hash RollingHash - }{ - { - "adler32", adler32.New(), - }, - { - "bozo32", bozo32.New(), - }, - { - "buzhash32", buzhash32.New(), - }, - { - "buzhash64", buzhash64.New(), - }, - } - - sizes := []int64{128 << 10, 16 << 20} - - for _, testSize := range sizes { - for _, test := range tests { - b.Run(test.name+"-"+fmt.Sprint(testSize), func(bb *testing.B) { - bb.Run("", func(bbb *testing.B) { - data := make([]byte, testSize) - - if _, err := test.hash.Write(data); err != nil { - bbb.Error(err) - } - - bbb.ResetTimer() - - for i := 0; i < bbb.N; i++ { - for j := int64(0); j <= testSize; j++ { - test.hash.Roll('a') - } - } - - bbb.SetBytes(testSize) - bbb.ReportAllocs() - }) - }) - } - } -} diff --git a/lib/weakhash/weakhash.go b/lib/weakhash/weakhash.go deleted file mode 100644 index 0ecce4c7b..000000000 --- a/lib/weakhash/weakhash.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright (C) 2016 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -package weakhash - -import ( - "bufio" - "context" - "io" - - "github.com/chmduquesne/rollinghash/adler32" -) - -const ( - Size = 4 - - // don't track more hits than this for any given weakhash - maxWeakhashFinderHits = 10 -) - -// Find finds all the blocks of the given size within io.Reader that matches -// the hashes provided, and returns a hash -> slice of offsets within reader -// map, that produces the same weak hash. -func Find(ctx context.Context, ir io.Reader, hashesToFind []uint32, size int) (map[uint32][]int64, error) { - if ir == nil || len(hashesToFind) == 0 { - return nil, nil - } - - r := bufio.NewReader(ir) - hf := adler32.New() - - n, err := io.CopyN(hf, r, int64(size)) - if err == io.EOF { - return nil, nil - } - if err != nil { - return nil, err - } - if n != int64(size) { - return nil, io.ErrShortBuffer - } - - offsets := make(map[uint32][]int64) - for _, hashToFind := range hashesToFind { - offsets[hashToFind] = make([]int64, 0, maxWeakhashFinderHits) - } - - var i int64 - var hash uint32 - for { - select { - case <-ctx.Done(): - return nil, ctx.Err() - default: - } - - hash = hf.Sum32() - if existing, ok := offsets[hash]; ok && len(existing) < maxWeakhashFinderHits { - offsets[hash] = append(existing, i) - } - i++ - - bt, err := r.ReadByte() - if err == io.EOF { - break - } else if err != nil { - return offsets, err - } - hf.Roll(bt) - } - return offsets, nil -} - -func NewFinder(ctx context.Context, ir io.ReadSeeker, size int, hashesToFind []uint32) (*Finder, error) { - offsets, err := Find(ctx, ir, hashesToFind, size) - if err != nil { - return nil, err - } - - return &Finder{ - reader: ir, - size: size, - offsets: offsets, - }, nil -} - -type Finder struct { - reader io.ReadSeeker - size int - offsets map[uint32][]int64 -} - -// Iterate iterates all available blocks that matches the provided hash, reads -// them into buf, and calls the iterator function. The iterator function should -// return whether it wishes to continue iterating. -func (h *Finder) Iterate(hash uint32, buf []byte, iterFunc func(int64) bool) (bool, error) { - if h == nil || hash == 0 || len(buf) != h.size { - return false, nil - } - - for _, offset := range h.offsets[hash] { - _, err := h.reader.Seek(offset, io.SeekStart) - if err != nil { - return false, err - } - _, err = h.reader.Read(buf) - if err != nil { - return false, err - } - if !iterFunc(offset) { - return true, nil - } - } - return false, nil -} diff --git a/lib/weakhash/weakhash_test.go b/lib/weakhash/weakhash_test.go deleted file mode 100644 index 78c2d3906..000000000 --- a/lib/weakhash/weakhash_test.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (C) 2016 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -// The existence of this file means we get 0% test coverage rather than no -// test coverage at all. Remove when implementing an actual test. - -package weakhash - -import ( - "bytes" - "context" - "io" - "os" - "reflect" - "testing" -) - -var payload = []byte("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz") - -func TestFinder(t *testing.T) { - f, err := os.CreateTemp("", "") - if err != nil { - t.Error(err) - } - defer os.Remove(f.Name()) - defer f.Close() - - if _, err := f.Write(payload); err != nil { - t.Error(err) - } - if _, err := f.Seek(0, io.SeekStart); err != nil { - t.Error(err) - } - - hashes := []uint32{65143183, 65798547} - finder, err := NewFinder(context.Background(), f, 4, hashes) - if err != nil { - t.Error(err) - } - - expected := map[uint32][]int64{ - 65143183: {1, 27, 53, 79}, - 65798547: {2, 28, 54, 80}, - } - actual := make(map[uint32][]int64) - - b := make([]byte, Size) - - for _, hash := range hashes { - _, err := finder.Iterate(hash, b[:4], func(offset int64) bool { - if !bytes.Equal(b, payload[offset:offset+4]) { - t.Errorf("Not equal at %d: %s != %s", offset, string(b), string(payload[offset:offset+4])) - } - actual[hash] = append(actual[hash], offset) - return true - }) - if err != nil { - t.Error(err) - } - } - - if !reflect.DeepEqual(actual, expected) { - t.Errorf("Not equal: %#v != %#v", actual, expected) - } -} diff --git a/meta/copyright_test.go b/meta/copyright_test.go index 8de266210..31078562a 100644 --- a/meta/copyright_test.go +++ b/meta/copyright_test.go @@ -19,11 +19,12 @@ import ( // File extensions to check var copyrightCheckExts = map[string]bool{ - ".go": true, + ".go": true, + ".sql": true, } // Directories to search -var copyrightCheckDirs = []string{".", "../cmd", "../lib", "../test", "../script"} +var copyrightCheckDirs = []string{".", "../cmd", "../internal", "../lib", "../test", "../script"} // Valid copyright headers, searched for in the top five lines in each file. var copyrightRegexps = []string{ diff --git a/next-gen-gui/.editorconfig b/next-gen-gui/.editorconfig deleted file mode 100644 index e89330a61..000000000 --- a/next-gen-gui/.editorconfig +++ /dev/null @@ -1,13 +0,0 @@ -# Editor configuration, see https://editorconfig.org -root = true - -[*] -charset = utf-8 -indent_style = space -indent_size = 2 -insert_final_newline = true -trim_trailing_whitespace = true - -[*.md] -max_line_length = off -trim_trailing_whitespace = false diff --git a/next-gen-gui/.gitignore b/next-gen-gui/.gitignore deleted file mode 100644 index 86d943a9b..000000000 --- a/next-gen-gui/.gitignore +++ /dev/null @@ -1,46 +0,0 @@ -# See http://help.github.com/ignore-files/ for more about ignoring files. - -# compiled output -/dist -/tmp -/out-tsc -# Only exists if Bazel was run -/bazel-out - -# dependencies -/node_modules - -# profiling files -chrome-profiler-events*.json -speed-measure-plugin*.json - -# IDEs and editors -/.idea -.project -.classpath -.c9/ -*.launch -.settings/ -*.sublime-workspace - -# IDE - VSCode -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -.history/* - -# misc -/.sass-cache -/connect.lock -/coverage -/libpeerconnection.log -npm-debug.log -yarn-error.log -testem.log -/typings - -# System Files -.DS_Store -Thumbs.db diff --git a/next-gen-gui/LICENSE b/next-gen-gui/LICENSE deleted file mode 100644 index d0a1fa148..000000000 --- a/next-gen-gui/LICENSE +++ /dev/null @@ -1,373 +0,0 @@ -Mozilla Public License Version 2.0 -================================== - -1. Definitions --------------- - -1.1. "Contributor" - means each individual or legal entity that creates, contributes to - the creation of, or owns Covered Software. - -1.2. "Contributor Version" - means the combination of the Contributions of others (if any) used - by a Contributor and that particular Contributor's Contribution. - -1.3. "Contribution" - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - means Source Code Form to which the initial Contributor has attached - the notice in Exhibit A, the Executable Form of such Source Code - Form, and Modifications of such Source Code Form, in each case - including portions thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - (a) that the initial Contributor has attached the notice described - in Exhibit B to the Covered Software; or - - (b) that the Covered Software was made available under the terms of - version 1.1 or earlier of the License, but not also under the - terms of a Secondary License. - -1.6. "Executable Form" - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - means a work that combines Covered Software with other material, in - a separate file or files, that is not Covered Software. - -1.8. "License" - means this document. - -1.9. "Licensable" - means having the right to grant, to the maximum extent possible, - whether at the time of the initial grant or subsequently, any and - all of the rights conveyed by this License. - -1.10. "Modifications" - means any of the following: - - (a) any file in Source Code Form that results from an addition to, - deletion from, or modification of the contents of Covered - Software; or - - (b) any new file in Source Code Form that contains any Covered - Software. - -1.11. "Patent Claims" of a Contributor - means any patent claim(s), including without limitation, method, - process, and apparatus claims, in any patent Licensable by such - Contributor that would be infringed, but for the grant of the - License, by the making, using, selling, offering for sale, having - made, import, or transfer of either its Contributions or its - Contributor Version. - -1.12. "Secondary License" - means either the GNU General Public License, Version 2.0, the GNU - Lesser General Public License, Version 2.1, the GNU Affero General - Public License, Version 3.0, or any later versions of those - licenses. - -1.13. "Source Code Form" - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that - controls, is controlled by, or is under common control with You. For - purposes of this definition, "control" means (a) the power, direct - or indirect, to cause the direction or management of such entity, - whether by contract or otherwise, or (b) ownership of more than - fifty percent (50%) of the outstanding shares or beneficial - ownership of such entity. - -2. License Grants and Conditions --------------------------------- - -2.1. Grants - -Each Contributor hereby grants You a world-wide, royalty-free, -non-exclusive license: - -(a) under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or - as part of a Larger Work; and - -(b) under Patent Claims of such Contributor to make, use, sell, offer - for sale, have made, import, and otherwise transfer either its - Contributions or its Contributor Version. - -2.2. Effective Date - -The licenses granted in Section 2.1 with respect to any Contribution -become effective for each Contribution on the date the Contributor first -distributes such Contribution. - -2.3. Limitations on Grant Scope - -The licenses granted in this Section 2 are the only rights granted under -this License. No additional rights or licenses will be implied from the -distribution or licensing of Covered Software under this License. -Notwithstanding Section 2.1(b) above, no patent license is granted by a -Contributor: - -(a) for any code that a Contributor has removed from Covered Software; - or - -(b) for infringements caused by: (i) Your and any other third party's - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - -(c) under Patent Claims infringed by Covered Software in the absence of - its Contributions. - -This License does not grant any rights in the trademarks, service marks, -or logos of any Contributor (except as may be necessary to comply with -the notice requirements in Section 3.4). - -2.4. Subsequent Licenses - -No Contributor makes additional grants as a result of Your choice to -distribute the Covered Software under a subsequent version of this -License (see Section 10.2) or under the terms of a Secondary License (if -permitted under the terms of Section 3.3). - -2.5. Representation - -Each Contributor represents that the Contributor believes its -Contributions are its original creation(s) or it has sufficient rights -to grant the rights to its Contributions conveyed by this License. - -2.6. Fair Use - -This License is not intended to limit any rights You have under -applicable copyright doctrines of fair use, fair dealing, or other -equivalents. - -2.7. Conditions - -Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted -in Section 2.1. - -3. Responsibilities -------------------- - -3.1. Distribution of Source Form - -All distribution of Covered Software in Source Code Form, including any -Modifications that You create or to which You contribute, must be under -the terms of this License. You must inform recipients that the Source -Code Form of the Covered Software is governed by the terms of this -License, and how they can obtain a copy of this License. You may not -attempt to alter or restrict the recipients' rights in the Source Code -Form. - -3.2. Distribution of Executable Form - -If You distribute Covered Software in Executable Form then: - -(a) such Covered Software must also be made available in Source Code - Form, as described in Section 3.1, and You must inform recipients of - the Executable Form how they can obtain a copy of such Source Code - Form by reasonable means in a timely manner, at a charge no more - than the cost of distribution to the recipient; and - -(b) You may distribute such Executable Form under the terms of this - License, or sublicense it under different terms, provided that the - license for the Executable Form does not attempt to limit or alter - the recipients' rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - -You may create and distribute a Larger Work under terms of Your choice, -provided that You also comply with the requirements of this License for -the Covered Software. If the Larger Work is a combination of Covered -Software with a work governed by one or more Secondary Licenses, and the -Covered Software is not Incompatible With Secondary Licenses, this -License permits You to additionally distribute such Covered Software -under the terms of such Secondary License(s), so that the recipient of -the Larger Work may, at their option, further distribute the Covered -Software under the terms of either this License or such Secondary -License(s). - -3.4. Notices - -You may not remove or alter the substance of any license notices -(including copyright notices, patent notices, disclaimers of warranty, -or limitations of liability) contained within the Source Code Form of -the Covered Software, except that You may alter any license notices to -the extent required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - -You may choose to offer, and to charge a fee for, warranty, support, -indemnity or liability obligations to one or more recipients of Covered -Software. However, You may do so only on Your own behalf, and not on -behalf of any Contributor. You must make it absolutely clear that any -such warranty, support, indemnity, or liability obligation is offered by -You alone, and You hereby agree to indemnify every Contributor for any -liability incurred by such Contributor as a result of warranty, support, -indemnity or liability terms You offer. You may include additional -disclaimers of warranty and limitations of liability specific to any -jurisdiction. - -4. Inability to Comply Due to Statute or Regulation ---------------------------------------------------- - -If it is impossible for You to comply with any of the terms of this -License with respect to some or all of the Covered Software due to -statute, judicial order, or regulation then You must: (a) comply with -the terms of this License to the maximum extent possible; and (b) -describe the limitations and the code they affect. Such description must -be placed in a text file included with all distributions of the Covered -Software under this License. Except to the extent prohibited by statute -or regulation, such description must be sufficiently detailed for a -recipient of ordinary skill to be able to understand it. - -5. Termination --------------- - -5.1. The rights granted under this License will terminate automatically -if You fail to comply with any of its terms. However, if You become -compliant, then the rights granted under this License from a particular -Contributor are reinstated (a) provisionally, unless and until such -Contributor explicitly and finally terminates Your grants, and (b) on an -ongoing basis, if such Contributor fails to notify You of the -non-compliance by some reasonable means prior to 60 days after You have -come back into compliance. Moreover, Your grants from a particular -Contributor are reinstated on an ongoing basis if such Contributor -notifies You of the non-compliance by some reasonable means, this is the -first time You have received notice of non-compliance with this License -from such Contributor, and You become compliant prior to 30 days after -Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent -infringement claim (excluding declaratory judgment actions, -counter-claims, and cross-claims) alleging that a Contributor Version -directly or indirectly infringes any patent, then the rights granted to -You by any and all Contributors for the Covered Software under Section -2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all -end user license agreements (excluding distributors and resellers) which -have been validly granted by You or Your distributors under this License -prior to termination shall survive termination. - -************************************************************************ -* * -* 6. Disclaimer of Warranty * -* ------------------------- * -* * -* Covered Software is provided under this License on an "as is" * -* basis, without warranty of any kind, either expressed, implied, or * -* statutory, including, without limitation, warranties that the * -* Covered Software is free of defects, merchantable, fit for a * -* particular purpose or non-infringing. The entire risk as to the * -* quality and performance of the Covered Software is with You. * -* Should any Covered Software prove defective in any respect, You * -* (not any Contributor) assume the cost of any necessary servicing, * -* repair, or correction. This disclaimer of warranty constitutes an * -* essential part of this License. No use of any Covered Software is * -* authorized under this License except under this disclaimer. * -* * -************************************************************************ - -************************************************************************ -* * -* 7. Limitation of Liability * -* -------------------------- * -* * -* Under no circumstances and under no legal theory, whether tort * -* (including negligence), contract, or otherwise, shall any * -* Contributor, or anyone who distributes Covered Software as * -* permitted above, be liable to You for any direct, indirect, * -* special, incidental, or consequential damages of any character * -* including, without limitation, damages for lost profits, loss of * -* goodwill, work stoppage, computer failure or malfunction, or any * -* and all other commercial damages or losses, even if such party * -* shall have been informed of the possibility of such damages. This * -* limitation of liability shall not apply to liability for death or * -* personal injury resulting from such party's negligence to the * -* extent applicable law prohibits such limitation. Some * -* jurisdictions do not allow the exclusion or limitation of * -* incidental or consequential damages, so this exclusion and * -* limitation may not apply to You. * -* * -************************************************************************ - -8. Litigation -------------- - -Any litigation relating to this License may be brought only in the -courts of a jurisdiction where the defendant maintains its principal -place of business and such litigation shall be governed by laws of that -jurisdiction, without reference to its conflict-of-law provisions. -Nothing in this Section shall prevent a party's ability to bring -cross-claims or counter-claims. - -9. Miscellaneous ----------------- - -This License represents the complete agreement concerning the subject -matter hereof. If any provision of this License is held to be -unenforceable, such provision shall be reformed only to the extent -necessary to make it enforceable. Any law or regulation which provides -that the language of a contract shall be construed against the drafter -shall not be used to construe this License against a Contributor. - -10. Versions of the License ---------------------------- - -10.1. New Versions - -Mozilla Foundation is the license steward. Except as provided in Section -10.3, no one other than the license steward has the right to modify or -publish new versions of this License. Each version will be given a -distinguishing version number. - -10.2. Effect of New Versions - -You may distribute the Covered Software under the terms of the version -of the License under which You originally received the Covered Software, -or under the terms of any subsequent version published by the license -steward. - -10.3. Modified Versions - -If you create software not governed by this License, and you want to -create a new license for such software, you may create and use a -modified version of this License if you rename the license and remove -any references to the name of the license steward (except to note that -such modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary -Licenses - -If You choose to distribute Source Code Form that is Incompatible With -Secondary Licenses under the terms of this version of the License, the -notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice -------------------------------------------- - - This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at https://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular -file, then You may include the notice in a location (such as a LICENSE -file in a relevant directory) where a recipient would be likely to look -for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - "Incompatible With Secondary Licenses" Notice ---------------------------------------------------------- - - This Source Code Form is "Incompatible With Secondary Licenses", as - defined by the Mozilla Public License, v. 2.0. diff --git a/next-gen-gui/README.md b/next-gen-gui/README.md deleted file mode 100644 index 4e747ac45..000000000 --- a/next-gen-gui/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# Syncthing Tech UI - -## Usage - -This is a very bare bones read-only GUI for viewing the status of large -setups. Download a [release -zip](https://github.com/kastelo/syncthing-tech-ui/releases) and unpack it -into the GUI override directory (assuming default Linux setup): - -``` -$ cd ~/.config/syncthing -$ mkdir -p gui/default -$ cd gui/default -$ unzip ~/tech-ui-v1.0.0.zip -``` - -Then load the GUI via http://localhost:8384/tech-ui/ or similar. You should see something like this: - -![Screenshot](screenshot.png) - -## Development server - -Run `npm run serve` for a dev server. Navigate to `http://localhost:4200/`. The -app will automatically reload if you change any of the source files. - -## Production server - -In production we serve the UI through Syncthing itself. The easiest way to -do that is to simply put the built assets in the `gui` subdirectory of -Syncthing's config directory. - -``` -$ npm run build -- --prod -$ rsync -va --delete dist/tech-ui/ ~/.config/syncthing/gui/default/tech-ui/ -``` - -Adjust for your actual Syncthing config dir if different. Navigate to -`http://localhost:8384/tech-ui/`. - -Another option is to start Syncthing with the STGUIASSETS environment -variable pointing to the distribution directory. - -``` -$ npm run build -- --prod -$ ln -sf . dist/default -$ export STGUIASSETS=$(pwd)/dist -$ syncthing -``` - -The magic is symlink is because Syncthing will look for the GUI in the -`default` subdirectory. Navigate to `http://localhost:8384/tech-ui/`. - -## Code scaffolding - -Run `ng generate component component-name` to generate a new component. You -can also use `ng generate -directive|pipe|service|class|guard|interface|enum|module`. - -## License - -MPLv2 - -## Copyright - -Copyright (c) 2020 The Syncthing Authors diff --git a/next-gen-gui/angular.json b/next-gen-gui/angular.json deleted file mode 100644 index e764afc38..000000000 --- a/next-gen-gui/angular.json +++ /dev/null @@ -1,128 +0,0 @@ -{ - "$schema": "./node_modules/@angular/cli/lib/config/schema.json", - "version": 1, - "newProjectRoot": "projects", - "projects": { - "next-gen-gui": { - "projectType": "application", - "schematics": { - "@schematics/angular:component": { - "style": "scss" - } - }, - "root": "", - "sourceRoot": "src", - "prefix": "app", - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:browser", - "options": { - "outputPath": "dist/next-gen-gui", - "index": "src/index.html", - "main": "src/main.ts", - "polyfills": "src/polyfills.ts", - "tsConfig": "tsconfig.app.json", - "aot": true, - "assets": [ - "src/favicon.ico", - "src/assets" - ], - "styles": [ - "src/styles.scss" - ], - "scripts": [] - }, - "configurations": { - "production": { - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.prod.ts" - } - ], - "optimization": true, - "outputHashing": "all", - "sourceMap": false, - "extractCss": true, - "namedChunks": false, - "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": true, - "budgets": [ - { - "type": "initial", - "maximumWarning": "2mb", - "maximumError": "5mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "6kb", - "maximumError": "10kb" - } - ] - } - } - }, - "serve": { - "builder": "@angular-devkit/build-angular:dev-server", - "options": { - "browserTarget": "next-gen-gui:build" - }, - "configurations": { - "production": { - "browserTarget": "next-gen-gui:build:production" - } - } - }, - "extract-i18n": { - "builder": "@angular-devkit/build-angular:extract-i18n", - "options": { - "browserTarget": "next-gen-gui:build" - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "src/test.ts", - "polyfills": "src/polyfills.ts", - "tsConfig": "tsconfig.spec.json", - "karmaConfig": "karma.conf.js", - "assets": [ - "src/favicon.ico", - "src/assets" - ], - "styles": [ - "src/styles.scss" - ], - "scripts": [] - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": [ - "tsconfig.app.json", - "tsconfig.spec.json", - "e2e/tsconfig.json" - ], - "exclude": [ - "**/node_modules/**" - ] - } - }, - "e2e": { - "builder": "@angular-devkit/build-angular:protractor", - "options": { - "protractorConfig": "e2e/protractor.conf.js", - "devServerTarget": "next-gen-gui:serve" - }, - "configurations": { - "production": { - "devServerTarget": "next-gen-gui:serve:production" - } - } - } - } - }}, - "defaultProject": "next-gen-gui" -} diff --git a/next-gen-gui/browserslist b/next-gen-gui/browserslist deleted file mode 100644 index 80848532e..000000000 --- a/next-gen-gui/browserslist +++ /dev/null @@ -1,12 +0,0 @@ -# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. -# For additional information regarding the format and rule options, please see: -# https://github.com/browserslist/browserslist#queries - -# You can see what browsers were selected by your queries by running: -# npx browserslist - -> 0.5% -last 2 versions -Firefox ESR -not dead -not IE 9-11 # For IE 9-11 support, remove 'not'. \ No newline at end of file diff --git a/next-gen-gui/e2e/protractor.conf.js b/next-gen-gui/e2e/protractor.conf.js deleted file mode 100644 index 7c798cfff..000000000 --- a/next-gen-gui/e2e/protractor.conf.js +++ /dev/null @@ -1,32 +0,0 @@ -// @ts-check -// Protractor configuration file, see link for more information -// https://github.com/angular/protractor/blob/master/lib/config.ts - -const { SpecReporter } = require('jasmine-spec-reporter'); - -/** - * @type { import("protractor").Config } - */ -exports.config = { - allScriptsTimeout: 11000, - specs: [ - './src/**/*.e2e-spec.ts' - ], - capabilities: { - browserName: 'chrome' - }, - directConnect: true, - baseUrl: 'http://localhost:4200/', - framework: 'jasmine', - jasmineNodeOpts: { - showColors: true, - defaultTimeoutInterval: 30000, - print: function() {} - }, - onPrepare() { - require('ts-node').register({ - project: require('path').join(__dirname, './tsconfig.json') - }); - jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); - } -}; \ No newline at end of file diff --git a/next-gen-gui/e2e/src/app.e2e-spec.ts b/next-gen-gui/e2e/src/app.e2e-spec.ts deleted file mode 100644 index 8e1d3c66f..000000000 --- a/next-gen-gui/e2e/src/app.e2e-spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { AppPage } from './app.po'; -import { browser, logging } from 'protractor'; - -describe('workspace-project App', () => { - let page: AppPage; - - beforeEach(() => { - page = new AppPage(); - }); - - it('should display welcome message', () => { - page.navigateTo(); - expect(page.getTitleText()).toEqual('tech-ui app is running!'); - }); - - afterEach(async () => { - // Assert that there are no errors emitted from the browser - const logs = await browser.manage().logs().get(logging.Type.BROWSER); - expect(logs).not.toContain(jasmine.objectContaining({ - level: logging.Level.SEVERE, - } as logging.Entry)); - }); -}); diff --git a/next-gen-gui/e2e/src/app.po.ts b/next-gen-gui/e2e/src/app.po.ts deleted file mode 100644 index b68475e0f..000000000 --- a/next-gen-gui/e2e/src/app.po.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { browser, by, element } from 'protractor'; - -export class AppPage { - navigateTo(): Promise { - return browser.get(browser.baseUrl) as Promise; - } - - getTitleText(): Promise { - return element(by.css('app-root .content span')).getText() as Promise; - } -} diff --git a/next-gen-gui/e2e/tsconfig.json b/next-gen-gui/e2e/tsconfig.json deleted file mode 100644 index 39b800f78..000000000 --- a/next-gen-gui/e2e/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "outDir": "../out-tsc/e2e", - "module": "commonjs", - "target": "es5", - "types": [ - "jasmine", - "jasminewd2", - "node" - ] - } -} diff --git a/next-gen-gui/karma.conf.js b/next-gen-gui/karma.conf.js deleted file mode 100644 index 0340dbacc..000000000 --- a/next-gen-gui/karma.conf.js +++ /dev/null @@ -1,32 +0,0 @@ -// Karma configuration file, see link for more information -// https://karma-runner.github.io/1.0/config/configuration-file.html - -module.exports = function (config) { - config.set({ - basePath: '', - frameworks: ['jasmine', '@angular-devkit/build-angular'], - plugins: [ - require('karma-jasmine'), - require('karma-chrome-launcher'), - require('karma-jasmine-html-reporter'), - require('karma-coverage-istanbul-reporter'), - require('@angular-devkit/build-angular/plugins/karma') - ], - client: { - clearContext: false // leave Jasmine Spec Runner output visible in browser - }, - coverageIstanbulReporter: { - dir: require('path').join(__dirname, './coverage/tech-ui'), - reports: ['html', 'lcovonly', 'text-summary'], - fixWebpackSourcePaths: true - }, - reporters: ['progress', 'kjhtml'], - port: 9876, - colors: true, - logLevel: config.LOG_INFO, - autoWatch: true, - browsers: ['Chrome'], - singleRun: false, - restartOnFileChange: true - }); -}; diff --git a/next-gen-gui/package-lock.json b/next-gen-gui/package-lock.json deleted file mode 100644 index 02f3f0c8d..000000000 --- a/next-gen-gui/package-lock.json +++ /dev/null @@ -1,37977 +0,0 @@ -{ - "name": "next-gen-gui", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "version": "1.0.0", - "dependencies": { - "@angular/animations": "^9.1.0", - "@angular/cdk": "^9.2.0", - "@angular/common": "^9.1.0", - "@angular/compiler": "^9.1.0", - "@angular/core": "^9.1.0", - "@angular/flex-layout": "^9.0.0-beta.29", - "@angular/forms": "^9.1.0", - "@angular/material": "^9.2.0", - "@angular/platform-browser": "^9.1.0", - "@angular/platform-browser-dynamic": "^9.1.0", - "@angular/router": "^9.1.0", - "angular-in-memory-web-api": "^0.10.0", - "chart.js": "^2.9.3", - "component": "^1.1.0", - "rxjs": "^6.5.5", - "tslib": "^1.10.0", - "zone.js": "^0.10.3" - }, - "devDependencies": { - "@angular-devkit/build-angular": "^0.901.0", - "@angular/cli": "^9.1.0", - "@angular/compiler-cli": "^9.1.0", - "@angular/language-service": "^9.1.0", - "@types/jasmine": "^3.5.10", - "@types/jasminewd2": "~2.0.3", - "@types/node": "^12.12.34", - "codelyzer": "^5.2.2", - "jasmine-core": "~3.5.0", - "jasmine-spec-reporter": "~4.2.1", - "karma": "~4.3.0", - "karma-chrome-launcher": "~3.1.0", - "karma-coverage-istanbul-reporter": "~2.1.0", - "karma-jasmine": "~2.0.1", - "karma-jasmine-html-reporter": "^1.5.3", - "protractor": "~5.4.3", - "ts-node": "~8.3.0", - "tslint": "~5.18.0", - "typescript": "~3.7.5" - } - }, - "node_modules/@angular-devkit/architect": { - "version": "0.901.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.901.0.tgz", - "integrity": "sha512-SlqEBkPrT40zMCy5344AsUqC76pEPCaGPaAkCIvadaz2dC9vNMzQrvubCPJHViD/TumkSX1kYmLS3iYASVM9GQ==", - "dev": true, - "dependencies": { - "@angular-devkit/core": "9.1.0", - "rxjs": "6.5.4" - }, - "engines": { - "node": ">= 10.13.0", - "npm": ">= 6.11.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-devkit/architect/node_modules/rxjs": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", - "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", - "dev": true, - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/@angular-devkit/build-angular": { - "version": "0.901.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.901.0.tgz", - "integrity": "sha512-ftJVNlKvIomqRfr5jFVraPqlLSUJu8YyVbFv/aCsvhNpuZGkYpTOMoJDwyywdslSTH608BIoU63IAnIz9PwUdw==", - "dev": true, - "dependencies": { - "@angular-devkit/architect": "0.901.0", - "@angular-devkit/build-optimizer": "0.901.0", - "@angular-devkit/build-webpack": "0.901.0", - "@angular-devkit/core": "9.1.0", - "@babel/core": "7.9.0", - "@babel/generator": "7.9.3", - "@babel/preset-env": "7.9.0", - "@babel/template": "7.8.6", - "@jsdevtools/coverage-istanbul-loader": "3.0.3", - "@ngtools/webpack": "9.1.0", - "ajv": "6.12.0", - "autoprefixer": "9.7.4", - "babel-loader": "8.0.6", - "browserslist": "^4.9.1", - "cacache": "15.0.0", - "caniuse-lite": "^1.0.30001032", - "circular-dependency-plugin": "5.2.0", - "copy-webpack-plugin": "5.1.1", - "core-js": "3.6.4", - "cssnano": "4.1.10", - "file-loader": "6.0.0", - "find-cache-dir": "3.3.1", - "glob": "7.1.6", - "jest-worker": "25.1.0", - "karma-source-map-support": "1.4.0", - "less": "3.11.1", - "less-loader": "5.0.0", - "license-webpack-plugin": "2.1.4", - "loader-utils": "2.0.0", - "mini-css-extract-plugin": "0.9.0", - "minimatch": "3.0.4", - "open": "7.0.3", - "parse5": "4.0.0", - "postcss": "7.0.27", - "postcss-import": "12.0.1", - "postcss-loader": "3.0.0", - "raw-loader": "4.0.0", - "regenerator-runtime": "0.13.5", - "rimraf": "3.0.2", - "rollup": "2.1.0", - "rxjs": "6.5.4", - "sass": "1.26.3", - "sass-loader": "8.0.2", - "semver": "7.1.3", - "source-map": "0.7.3", - "source-map-loader": "0.2.4", - "source-map-support": "0.5.16", - "speed-measure-webpack-plugin": "1.3.1", - "style-loader": "1.1.3", - "stylus": "0.54.7", - "stylus-loader": "3.0.2", - "terser": "4.6.7", - "terser-webpack-plugin": "2.3.5", - "tree-kill": "1.2.2", - "webpack": "4.42.0", - "webpack-dev-middleware": "3.7.2", - "webpack-dev-server": "3.10.3", - "webpack-merge": "4.2.2", - "webpack-sources": "1.4.3", - "webpack-subresource-integrity": "1.4.0", - "worker-plugin": "4.0.2" - }, - "engines": { - "node": ">= 10.13.0", - "npm": ">= 6.11.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "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" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/parse5": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", - "dev": true - }, - "node_modules/@angular-devkit/build-angular/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/postcss/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/regenerator-runtime": { - "version": "0.13.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", - "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==", - "dev": true - }, - "node_modules/@angular-devkit/build-angular/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/rxjs": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", - "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", - "dev": true, - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/semver": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", - "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@angular-devkit/build-optimizer": { - "version": "0.901.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.901.0.tgz", - "integrity": "sha512-Y9sz8uf2zjilhPUVYb0K9Mio6c1d5c+csuDc15CCKzELXJwyyDxilIFgn6Eu+edM0HNQGzbIwkjy4DkR9mtuTQ==", - "dev": true, - "dependencies": { - "loader-utils": "2.0.0", - "source-map": "0.7.3", - "tslib": "1.11.1", - "typescript": "3.8.3", - "webpack-sources": "1.4.3" - }, - "bin": { - "build-optimizer": "src/build-optimizer/cli.js" - }, - "engines": { - "node": ">= 10.13.0", - "npm": ">= 6.11.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-devkit/build-optimizer/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@angular-devkit/build-optimizer/node_modules/typescript": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", - "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/@angular-devkit/build-webpack": { - "version": "0.901.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.901.0.tgz", - "integrity": "sha512-Oze0VzIvHnoW12C80fiNH4HBu/GWmhJPXdNA7nRkU/tBQlIKnfngf8rQ0QbgecN2qdEXQpZJsP/XclTi3zugsg==", - "dev": true, - "dependencies": { - "@angular-devkit/architect": "0.901.0", - "@angular-devkit/core": "9.1.0", - "rxjs": "6.5.4" - }, - "engines": { - "node": ">= 10.13.0", - "npm": ">= 6.11.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-devkit/build-webpack/node_modules/rxjs": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", - "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", - "dev": true, - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/@angular-devkit/core": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-9.1.0.tgz", - "integrity": "sha512-vHTsrB4JaVUQ95FRnKrgo79Y3F6FokImrZdrmwkQmwAThpjXeXmpUEKZS+ZSTFRgesjiIysVGOFijARP4BQ7Bg==", - "dev": true, - "dependencies": { - "ajv": "6.12.0", - "fast-json-stable-stringify": "2.1.0", - "magic-string": "0.25.7", - "rxjs": "6.5.4", - "source-map": "0.7.3" - }, - "engines": { - "node": ">= 10.13.0", - "npm": ">= 6.11.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-devkit/core/node_modules/rxjs": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", - "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", - "dev": true, - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/@angular-devkit/core/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@angular-devkit/schematics": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-9.1.0.tgz", - "integrity": "sha512-cb9PSvskMwWlL54fPfCcpJoyNDWAX6Wo7CzL5qpIB2cJCPLAuyfRUYYrkO77YUST+n2HvypHz0cZ5SNGMfaaBQ==", - "dev": true, - "dependencies": { - "@angular-devkit/core": "9.1.0", - "ora": "4.0.3", - "rxjs": "6.5.4" - }, - "engines": { - "node": ">= 10.13.0", - "npm": ">= 6.11.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-devkit/schematics/node_modules/rxjs": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", - "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", - "dev": true, - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/@angular/animations": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-9.1.0.tgz", - "integrity": "sha512-o7X3HM+eocoryw3VrDUtG6Wci2KwtzyBFo3KBJXjQ16X6fwdkjTG+hLb7pp2CBFBEJW4tPYEy7cSBmEfMRTqag==" - }, - "node_modules/@angular/cdk": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-9.2.0.tgz", - "integrity": "sha512-jeeznvNDpR9POuxzz8Y0zFvMynG9HCJo3ZPTqOjlOq8Lj8876+rLsHDvKEMeLdwlkdi1EweYJW1CLQzI+TwqDA==", - "dependencies": { - "parse5": "^5.0.0" - }, - "optionalDependencies": { - "parse5": "^5.0.0" - } - }, - "node_modules/@angular/cli": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-9.1.0.tgz", - "integrity": "sha512-ofum4gPE/W3fKyzuJrpdHeOS0ZL8x0eYCgsrMyUoFodSpb5LWPqeW+56NgDTpIeny+Trx3pM9dr9QTUVTJ0vYg==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "@angular-devkit/architect": "0.901.0", - "@angular-devkit/core": "9.1.0", - "@angular-devkit/schematics": "9.1.0", - "@schematics/angular": "9.1.0", - "@schematics/update": "0.901.0", - "@yarnpkg/lockfile": "1.1.0", - "ansi-colors": "4.1.1", - "debug": "4.1.1", - "ini": "1.3.5", - "inquirer": "7.1.0", - "npm-package-arg": "8.0.1", - "npm-pick-manifest": "6.0.0", - "open": "7.0.3", - "pacote": "11.1.4", - "read-package-tree": "5.3.1", - "rimraf": "3.0.2", - "semver": "7.1.3", - "symbol-observable": "1.2.0", - "universal-analytics": "0.4.20", - "uuid": "7.0.2" - }, - "bin": { - "ng": "bin/ng" - }, - "engines": { - "node": ">= 10.13.0", - "npm": ">= 6.11.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular/cli/node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@angular/cli/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "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" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@angular/cli/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@angular/cli/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/@angular/cli/node_modules/semver": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", - "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@angular/cli/node_modules/uuid": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.2.tgz", - "integrity": "sha512-vy9V/+pKG+5ZTYKf+VcphF5Oc6EFiu3W8Nv3P3zIh0EqVI80ZxOzuPfe9EHjkFNvf8+xuTHVeei4Drydlx4zjw==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/@angular/common": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-9.1.0.tgz", - "integrity": "sha512-6JPLNtMhI03bGTVQJeSwc+dTjV6DtP7M/BAyzIV0InZP1D6XsOh2QahLFIaaN2sSxYA2ClKuwfX1v+rx9AbXQA==" - }, - "node_modules/@angular/compiler": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-9.1.0.tgz", - "integrity": "sha512-QHw/JSeTXHiJQ2Ih0EtU7FGsYcOr+0hwZhqwSW3EEn8TtUgA3DS5lXeiDV66f+3DdvNZFPmgiZIvun3ypxn1HA==" - }, - "node_modules/@angular/compiler-cli": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-9.1.0.tgz", - "integrity": "sha512-xZ8mVPmPporSTtvNA+cbFJQymLzuWfMX6HDDgztZ2eZ5WcQJYloRN4CcYMEzDhCxfV1Zw9Tfc2l14jZD8osi6g==", - "dev": true, - "dependencies": { - "canonical-path": "1.0.0", - "chokidar": "^3.0.0", - "convert-source-map": "^1.5.1", - "dependency-graph": "^0.7.2", - "fs-extra": "4.0.2", - "magic-string": "^0.25.0", - "minimist": "^1.2.0", - "reflect-metadata": "^0.1.2", - "semver": "^6.3.0", - "source-map": "^0.6.1", - "sourcemap-codec": "^1.4.8", - "yargs": "15.3.0" - }, - "bin": { - "ivy-ngcc": "ngcc/main-ivy-ngcc.js", - "ng-xi18n": "src/extract_i18n.js", - "ngc": "src/main.js", - "ngcc": "ngcc/main-ngcc.js" - }, - "engines": { - "node": ">=10.0" - } - }, - "node_modules/@angular/compiler-cli/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@angular/compiler-cli/node_modules/ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "dependencies": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@angular/compiler-cli/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@angular/compiler-cli/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/@angular/compiler-cli/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@angular/compiler-cli/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@angular/compiler-cli/node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/@angular/compiler-cli/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@angular/compiler-cli/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@angular/compiler-cli/node_modules/p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@angular/compiler-cli/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@angular/compiler-cli/node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@angular/compiler-cli/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@angular/compiler-cli/node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "node_modules/@angular/compiler-cli/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@angular/compiler-cli/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@angular/compiler-cli/node_modules/string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@angular/compiler-cli/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@angular/compiler-cli/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@angular/compiler-cli/node_modules/y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "node_modules/@angular/compiler-cli/node_modules/yargs": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.0.tgz", - "integrity": "sha512-g/QCnmjgOl1YJjGsnUg2SatC7NUYEiLXJqxNOQU9qSpjzGtGXda9b+OKccr1kLTy8BN9yqEyqfq5lxlwdc13TA==", - "dev": true, - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@angular/compiler-cli/node_modules/yargs-parser": { - "version": "18.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.2.tgz", - "integrity": "sha512-hlIPNR3IzC1YuL1c2UwwDKpXlNFBqD1Fswwh1khz5+d8Cq/8yc/Mn0i+rQXduu8hcrFKvO7Eryk+09NecTQAAQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@angular/core": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-9.1.0.tgz", - "integrity": "sha512-RVlyegdIAij0P1wLY5ObIdsBAzvmHkHfElnmfiNKhaDftP6U/3zRtaKDu0bq0jvn1WCQ8zXxFQ8AWyKZwyFS+w==" - }, - "node_modules/@angular/flex-layout": { - "version": "9.0.0-beta.29", - "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-9.0.0-beta.29.tgz", - "integrity": "sha512-93sxR+kYfYMOdnlWL0Q77FZ428gg8XnBu0YZm6GsCdkw/vLggIT/G1ZAqHlCPIODt6pxmCJ5KXh4ShvniIYDsA==" - }, - "node_modules/@angular/forms": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-9.1.0.tgz", - "integrity": "sha512-5GC8HQlPChPV+168zLlm4yj4syA6N9ChSKV0tmzj1zIfMcub1UAOaB9IYaXRHQsjPFh9OuQXwmkzScyAfhEVjA==" - }, - "node_modules/@angular/language-service": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-9.1.0.tgz", - "integrity": "sha512-2f8ECoXrj40oS1rtIfi+F8T4WPzundcZDs8WMFNBuWYbk14v1S9sTgMEmZyePHGkPjt6IfYiLJKJCvVgrt1nxQ==", - "dev": true - }, - "node_modules/@angular/material": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-9.2.0.tgz", - "integrity": "sha512-KKzEIVh6/m56m+Ao8p4PK0SyEr0574l3VP2swj1qPag3u+FYgemmXCGTaChrKdDsez+zeTCPXImBGXzE6NQ80Q==" - }, - "node_modules/@angular/platform-browser": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-9.1.0.tgz", - "integrity": "sha512-OsS/blUjl8ranmDaRADjFAmvnlmwbT6WNU7dVov7FhV0rqesbwaOJ5bR0LSYHYpej7Jaa6oYk0v0XWkaH9LTFg==" - }, - "node_modules/@angular/platform-browser-dynamic": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-9.1.0.tgz", - "integrity": "sha512-sMtz/poQ3TYaWZzWjrn9apKUZ/WKql2MYCWbpax7pql3GgC9OoTslc7ZEe7/d3ynfFE/CQqWBBOuWGD71Z0LMQ==" - }, - "node_modules/@angular/router": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-9.1.0.tgz", - "integrity": "sha512-cExO1nPnoPFiUJWZ28hTHozPLFoCmqr3xqcM57We0hhKE0esdrO+gRWKRH0EJERukLbU8coPKVhA8daGUpASiQ==" - }, - "node_modules/@babel/code-frame": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", - "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.8.3" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.9.0.tgz", - "integrity": "sha512-zeFQrr+284Ekvd9e7KAX954LkapWiOmQtsfHirhxqfdlX6MEC32iRE+pqUGlYIBchdevaCwvzxWGSy/YBNI85g==", - "dev": true, - "dependencies": { - "browserslist": "^4.9.1", - "invariant": "^2.2.4", - "semver": "^5.5.0" - } - }, - "node_modules/@babel/compat-data/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/@babel/core": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.0.tgz", - "integrity": "sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.9.0", - "@babel/helper-module-transforms": "^7.9.0", - "@babel/helpers": "^7.9.0", - "@babel/parser": "^7.9.0", - "@babel/template": "^7.8.6", - "@babel/traverse": "^7.9.0", - "@babel/types": "^7.9.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.13", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/@babel/core/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@babel/generator": { - "version": "7.9.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.3.tgz", - "integrity": "sha512-RpxM252EYsz9qLUIq6F7YJyK1sv0wWDBFuztfDGWaQKzHjqDHysxSiRUpA/X9jmfqo+WzkAVKFaUily5h+gDCQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.9.0", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "node_modules/@babel/generator/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz", - "integrity": "sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.8.3" - } - }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz", - "integrity": "sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw==", - "dev": true, - "dependencies": { - "@babel/helper-explode-assignable-expression": "^7.8.3", - "@babel/types": "^7.8.3" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.7.tgz", - "integrity": "sha512-4mWm8DCK2LugIS+p1yArqvG1Pf162upsIsjE7cNBjez+NjliQpVhj20obE520nao0o14DaTnFJv+Fw5a0JpoUw==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.8.6", - "browserslist": "^4.9.1", - "invariant": "^2.2.4", - "levenary": "^1.1.1", - "semver": "^5.5.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.8.8", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.8.tgz", - "integrity": "sha512-LYVPdwkrQEiX9+1R29Ld/wTrmQu1SSKYnuOk3g0CkcZMA1p0gsNxJFj/3gBdaJ7Cg0Fnek5z0DsMULePP7Lrqg==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.8.3", - "@babel/helper-regex": "^7.8.3", - "regexpu-core": "^4.7.0" - } - }, - "node_modules/@babel/helper-define-map": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz", - "integrity": "sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g==", - "dev": true, - "dependencies": { - "@babel/helper-function-name": "^7.8.3", - "@babel/types": "^7.8.3", - "lodash": "^4.17.13" - } - }, - "node_modules/@babel/helper-explode-assignable-expression": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz", - "integrity": "sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.8.3", - "@babel/types": "^7.8.3" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", - "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", - "dev": true, - "dependencies": { - "@babel/helper-get-function-arity": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/types": "^7.8.3" - } - }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", - "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.8.3" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz", - "integrity": "sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.8.3" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz", - "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.8.3" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz", - "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.8.3" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz", - "integrity": "sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.6", - "@babel/helper-simple-access": "^7.8.3", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/template": "^7.8.6", - "@babel/types": "^7.9.0", - "lodash": "^4.17.13" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz", - "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.8.3" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - }, - "node_modules/@babel/helper-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.8.3.tgz", - "integrity": "sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ==", - "dev": true, - "dependencies": { - "lodash": "^4.17.13" - } - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz", - "integrity": "sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA==", - "dev": true, - "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" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz", - "integrity": "sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA==", - "dev": true, - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.8.3", - "@babel/helper-optimise-call-expression": "^7.8.3", - "@babel/traverse": "^7.8.6", - "@babel/types": "^7.8.6" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz", - "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.8.3", - "@babel/types": "^7.8.3" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", - "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.8.3" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz", - "integrity": "sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==", - "dev": true - }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz", - "integrity": "sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ==", - "dev": true, - "dependencies": { - "@babel/helper-function-name": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.8.3", - "@babel/types": "^7.8.3" - } - }, - "node_modules/@babel/helpers": { - "version": "7.9.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.2.tgz", - "integrity": "sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA==", - "dev": true, - "dependencies": { - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.9.0", - "@babel/types": "^7.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", - "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.9.0", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.9.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", - "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz", - "integrity": "sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw==", - "dev": true, - "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" - } - }, - "node_modules/@babel/plugin-proposal-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz", - "integrity": "sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-dynamic-import": "^7.8.0" - } - }, - "node_modules/@babel/plugin-proposal-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz", - "integrity": "sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.0" - } - }, - "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" - } - }, - "node_modules/@babel/plugin-proposal-numeric-separator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz", - "integrity": "sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3" - } - }, - "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.0.tgz", - "integrity": "sha512-UgqBv6bjq4fDb8uku9f+wcm1J7YxJ5nT7WO/jBr0cl0PLKb7t1O6RNR1kZbjgx2LQtsDI9hwoQVmn0yhXeQyow==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0" - } - }, - "node_modules/@babel/plugin-proposal-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" - } - }, - "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz", - "integrity": "sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.0" - } - }, - "node_modules/@babel/plugin-proposal-unicode-property-regex": { - "version": "7.8.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.8.tgz", - "integrity": "sha512-EVhjVsMpbhLw9ZfHWSx2iy13Q8Z/eg8e8ccVWt23sWQK5l1UdkoLJPN5w69UA4uITGBnEZD2JOe4QOHycYKv8A==", - "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.8.8", - "@babel/helper-plugin-utils": "^7.8.3" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz", - "integrity": "sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz", - "integrity": "sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz", - "integrity": "sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz", - "integrity": "sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-remap-async-to-generator": "^7.8.3" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz", - "integrity": "sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz", - "integrity": "sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3", - "lodash": "^4.17.13" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.9.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.2.tgz", - "integrity": "sha512-TC2p3bPzsfvSsqBZo0kJnuelnoK9O3welkUpqSqBQuBF6R5MN2rysopri8kNvtlGIb2jmUO7i15IooAZJjZuMQ==", - "dev": true, - "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.6", - "@babel/helper-split-export-declaration": "^7.8.3", - "globals": "^11.1.0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz", - "integrity": "sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.8.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.8.8.tgz", - "integrity": "sha512-eRJu4Vs2rmttFCdhPUM3bV0Yo/xPSdPw6ML9KHs/bjB4bLA5HXlbvYXPOD5yASodGod+krjYx21xm1QmL8dCJQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz", - "integrity": "sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw==", - "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz", - "integrity": "sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz", - "integrity": "sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ==", - "dev": true, - "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.9.0.tgz", - "integrity": "sha512-lTAnWOpMwOXpyDx06N+ywmF3jNbafZEqZ96CGYabxHrxNX8l5ny7dt4bK/rGwAh9utyP2b2Hv7PlZh1AAS54FQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz", - "integrity": "sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ==", - "dev": true, - "dependencies": { - "@babel/helper-function-name": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz", - "integrity": "sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz", - "integrity": "sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.9.0.tgz", - "integrity": "sha512-vZgDDF003B14O8zJy0XXLnPH4sg+9X5hFBBGN1V+B2rgrB+J2xIypSN6Rk9imB2hSTHQi5OHLrFWsZab1GMk+Q==", - "dev": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.9.0", - "@babel/helper-plugin-utils": "^7.8.3", - "babel-plugin-dynamic-import-node": "^2.3.0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.0.tgz", - "integrity": "sha512-qzlCrLnKqio4SlgJ6FMMLBe4bySNis8DFn1VkGmOcxG9gqEyPIOzeQrA//u0HAKrWpJlpZbZMPB1n/OPa4+n8g==", - "dev": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.9.0", - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-simple-access": "^7.8.3", - "babel-plugin-dynamic-import-node": "^2.3.0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.9.0.tgz", - "integrity": "sha512-FsiAv/nao/ud2ZWy4wFacoLOm5uxl0ExSQ7ErvP7jpoihLR6Cq90ilOFyX9UXct3rbtKsAiZ9kFt5XGfPe/5SQ==", - "dev": true, - "dependencies": { - "@babel/helper-hoist-variables": "^7.8.3", - "@babel/helper-module-transforms": "^7.9.0", - "@babel/helper-plugin-utils": "^7.8.3", - "babel-plugin-dynamic-import-node": "^2.3.0" - } - }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.9.0.tgz", - "integrity": "sha512-uTWkXkIVtg/JGRSIABdBoMsoIeoHQHPTL0Y2E7xf5Oj7sLqwVsNXOkNk0VJc7vF0IMBsPeikHxFjGe+qmwPtTQ==", - "dev": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.9.0", - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz", - "integrity": "sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw==", - "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.8.3" - } - }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz", - "integrity": "sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz", - "integrity": "sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.3" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.9.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.3.tgz", - "integrity": "sha512-fzrQFQhp7mIhOzmOtPiKffvCYQSK10NR8t6BBz2yPbeUHb9OLW8RZGtgDRBn8z2hGcwvKDL3vC7ojPTLNxmqEg==", - "dev": true, - "dependencies": { - "@babel/helper-get-function-arity": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz", - "integrity": "sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz", - "integrity": "sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA==", - "dev": true, - "dependencies": { - "regenerator-transform": "^0.14.2" - } - }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz", - "integrity": "sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz", - "integrity": "sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz", - "integrity": "sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz", - "integrity": "sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-regex": "^7.8.3" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz", - "integrity": "sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz", - "integrity": "sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz", - "integrity": "sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw==", - "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "node_modules/@babel/preset-env": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.9.0.tgz", - "integrity": "sha512-712DeRXT6dyKAM/FMbQTV/FvRCms2hPCx+3weRjZ8iQVQWZejWWk1wwG6ViWMyqb/ouBbGOl5b6aCk0+j1NmsQ==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.9.0", - "@babel/helper-compilation-targets": "^7.8.7", - "@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-numeric-separator": "^7.8.3", - "@babel/plugin-proposal-object-rest-spread": "^7.9.0", - "@babel/plugin-proposal-optional-catch-binding": "^7.8.3", - "@babel/plugin-proposal-optional-chaining": "^7.9.0", - "@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-numeric-separator": "^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.9.0", - "@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.9.0", - "@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.9.0", - "@babel/plugin-transform-modules-commonjs": "^7.9.0", - "@babel/plugin-transform-modules-systemjs": "^7.9.0", - "@babel/plugin-transform-modules-umd": "^7.9.0", - "@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.7", - "@babel/plugin-transform-property-literals": "^7.8.3", - "@babel/plugin-transform-regenerator": "^7.8.7", - "@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/preset-modules": "^0.1.3", - "@babel/types": "^7.9.0", - "browserslist": "^4.9.1", - "core-js-compat": "^3.6.2", - "invariant": "^2.2.2", - "levenary": "^1.1.1", - "semver": "^5.5.0" - } - }, - "node_modules/@babel/preset-env/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/@babel/preset-modules": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.3.tgz", - "integrity": "sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "node_modules/@babel/runtime": { - "version": "7.9.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz", - "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==", - "dev": true, - "dependencies": { - "regenerator-runtime": "^0.13.4" - } - }, - "node_modules/@babel/runtime/node_modules/regenerator-runtime": { - "version": "0.13.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", - "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==", - "dev": true - }, - "node_modules/@babel/template": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", - "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.8.3", - "@babel/parser": "^7.8.6", - "@babel/types": "^7.8.6" - } - }, - "node_modules/@babel/traverse": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.0.tgz", - "integrity": "sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.9.0", - "@babel/helper-function-name": "^7.8.3", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/parser": "^7.9.0", - "@babel/types": "^7.9.0", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } - }, - "node_modules/@babel/types": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.0.tgz", - "integrity": "sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.9.0", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jsdevtools/coverage-istanbul-loader": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.3.tgz", - "integrity": "sha512-TAdNkeGB5Fe4Og+ZkAr1Kvn9by2sfL44IAHFtxlh1BA1XJ5cLpO9iSNki5opWESv3l3vSHsZ9BNKuqFKbEbFaA==", - "dev": true, - "dependencies": { - "convert-source-map": "^1.7.0", - "istanbul-lib-instrument": "^4.0.1", - "loader-utils": "^1.4.0", - "merge-source-map": "^1.1.0", - "schema-utils": "^2.6.4" - } - }, - "node_modules/@jsdevtools/coverage-istanbul-loader/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/@jsdevtools/coverage-istanbul-loader/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/@ngtools/webpack": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-9.1.0.tgz", - "integrity": "sha512-kQ+1N/F+5tuUXiiaoqJwhcOIM0I93EEvF3xwpTLRm91wl2i8R1261LvsD/uQPrgLrZNGR6eFhFF1Izn2PnIjQA==", - "dev": true, - "dependencies": { - "@angular-devkit/core": "9.1.0", - "enhanced-resolve": "4.1.1", - "rxjs": "6.5.4", - "webpack-sources": "1.4.3" - }, - "engines": { - "node": ">= 10.13.0", - "npm": ">= 6.11.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@ngtools/webpack/node_modules/rxjs": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", - "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", - "dev": true, - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/@npmcli/ci-detect": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@npmcli/ci-detect/-/ci-detect-1.2.0.tgz", - "integrity": "sha512-JtktVH7ASBVIWsQTFlFpeOzhBJskvoBCTfeeRhhZy7ybATcUvwiwotZ8j5rkqUUyB69lIy/AvboiiiGBjYBKBA==", - "dev": true - }, - "node_modules/@npmcli/git": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.0.1.tgz", - "integrity": "sha512-hVatexiBtx71F01Ars38Hr5AFUGmJgHAfQtRlO5fJlnAawRGSXwEFgjB5i3XdUUmElZU/RXy7fefN02dZKxgPw==", - "dev": true, - "dependencies": { - "@npmcli/promise-spawn": "^1.1.0", - "mkdirp": "^1.0.3", - "npm-pick-manifest": "^6.0.0", - "promise-inflight": "^1.0.1", - "promise-retry": "^1.1.1", - "unique-filename": "^1.1.1", - "which": "^2.0.2" - } - }, - "node_modules/@npmcli/git/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@npmcli/git/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@npmcli/installed-package-contents": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.5.tgz", - "integrity": "sha512-aKIwguaaqb6ViwSOFytniGvLPb9SMCUm39TgM3SfUo7n0TxUMbwoXfpwyvQ4blm10lzbAwTsvjr7QZ85LvTi4A==", - "dev": true, - "dependencies": { - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1", - "read-package-json-fast": "^1.1.1", - "readdir-scoped-modules": "^1.1.0" - }, - "bin": { - "installed-package-contents": "index.js" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@npmcli/promise-spawn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.1.0.tgz", - "integrity": "sha512-FwbuYN9KXBkloLeIR3xRgI8dyOdfK/KzaJlChszNuwmUXD1lHXfLlSeo4n4KrKt2udIK9K9/TzlnyCA3ubM2fA==", - "dev": true, - "dependencies": { - "infer-owner": "^1.0.4" - } - }, - "node_modules/@schematics/angular": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-9.1.0.tgz", - "integrity": "sha512-qkehaITQ1S1udfnnBY5CXGWnk1iVFI8cZayjLUlRfD5w+6v9if3VIuqPssX96MqvkbjyRu1N214+ieaawzLmuA==", - "dev": true, - "dependencies": { - "@angular-devkit/core": "9.1.0", - "@angular-devkit/schematics": "9.1.0" - }, - "engines": { - "node": ">= 10.13.0", - "npm": ">= 6.11.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@schematics/update": { - "version": "0.901.0", - "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.901.0.tgz", - "integrity": "sha512-u2VESL1dgOSGZK/wcWEz0WcCU/yv764zhzCQerCwUtbV1CISSSDZ6x+prVYDXOdxWBGtDos2MbCF3GEJJI1T+w==", - "dev": true, - "dependencies": { - "@angular-devkit/core": "9.1.0", - "@angular-devkit/schematics": "9.1.0", - "@yarnpkg/lockfile": "1.1.0", - "ini": "1.3.5", - "npm-package-arg": "^8.0.0", - "pacote": "11.1.4", - "rxjs": "6.5.4", - "semver": "7.1.3", - "semver-intersect": "1.4.0" - }, - "engines": { - "node": ">= 10.13.0", - "npm": ">= 6.11.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@schematics/update/node_modules/rxjs": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", - "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", - "dev": true, - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/@schematics/update/node_modules/semver": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", - "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@tootallnate/once": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.0.0.tgz", - "integrity": "sha512-KYyTT/T6ALPkIRd2Ge080X/BsXvy9O0hcWTtMWkPvwAwF99+vn6Dv4GzrFT/Nn1LePr+FFDbRXXlqmsy9lw2zA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", - "dev": true - }, - "node_modules/@types/events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", - "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", - "dev": true - }, - "node_modules/@types/glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", - "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", - "dev": true, - "dependencies": { - "@types/events": "*", - "@types/minimatch": "*", - "@types/node": "*" - } - }, - "node_modules/@types/jasmine": { - "version": "3.5.10", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.5.10.tgz", - "integrity": "sha512-3F8qpwBAiVc5+HPJeXJpbrl+XjawGmciN5LgiO7Gv1pl1RHtjoMNqZpqEksaPJW05ViKe8snYInRs6xB25Xdew==", - "dev": true - }, - "node_modules/@types/jasminewd2": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.8.tgz", - "integrity": "sha512-d9p31r7Nxk0ZH0U39PTH0hiDlJ+qNVGjlt1ucOoTUptxb2v+Y5VMnsxfwN+i3hK4yQnqBi3FMmoMFcd1JHDxdg==", - "dev": true, - "dependencies": { - "@types/jasmine": "*" - } - }, - "node_modules/@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", - "dev": true - }, - "node_modules/@types/node": { - "version": "12.12.34", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.34.tgz", - "integrity": "sha512-BneGN0J9ke24lBRn44hVHNeDlrXRYF+VRp0HbSUNnEZahXGAysHZIqnf/hER6aabdBgzM4YOV4jrR8gj4Zfi0g==", - "dev": true - }, - "node_modules/@types/q": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz", - "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==", - "dev": true - }, - "node_modules/@types/selenium-webdriver": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.17.tgz", - "integrity": "sha512-tGomyEuzSC1H28y2zlW6XPCaDaXFaD6soTdb4GNdmte2qfHtrKqhy0ZFs4r/1hpazCfEZqeTSRLvSasmEx89uw==", - "dev": true - }, - "node_modules/@types/source-list-map": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", - "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", - "dev": true - }, - "node_modules/@types/webpack-sources": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.7.tgz", - "integrity": "sha512-XyaHrJILjK1VHVC4aVlKsdNN5KBTwufMb43cQs+flGxtPAf/1Qwl8+Q0tp5BwEGaI8D6XT1L+9bSWXckgkjTLw==", - "dev": true, - "dependencies": { - "@types/node": "*", - "@types/source-list-map": "*", - "source-map": "^0.6.1" - } - }, - "node_modules/@types/webpack-sources/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", - "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==", - "dev": true, - "dependencies": { - "@webassemblyjs/helper-module-context": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/wast-parser": "1.8.5" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz", - "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz", - "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz", - "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-code-frame": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz", - "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==", - "dev": true, - "dependencies": { - "@webassemblyjs/wast-printer": "1.8.5" - } - }, - "node_modules/@webassemblyjs/helper-fsm": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz", - "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-module-context": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz", - "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.8.5", - "mamacro": "^0.0.3" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz", - "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz", - "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz", - "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==", - "dev": true, - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz", - "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==", - "dev": true, - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz", - "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==", - "dev": true - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz", - "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/helper-wasm-section": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5", - "@webassemblyjs/wasm-opt": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5", - "@webassemblyjs/wast-printer": "1.8.5" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz", - "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/ieee754": "1.8.5", - "@webassemblyjs/leb128": "1.8.5", - "@webassemblyjs/utf8": "1.8.5" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz", - "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz", - "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-api-error": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/ieee754": "1.8.5", - "@webassemblyjs/leb128": "1.8.5", - "@webassemblyjs/utf8": "1.8.5" - } - }, - "node_modules/@webassemblyjs/wast-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz", - "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/floating-point-hex-parser": "1.8.5", - "@webassemblyjs/helper-api-error": "1.8.5", - "@webassemblyjs/helper-code-frame": "1.8.5", - "@webassemblyjs/helper-fsm": "1.8.5", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz", - "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/wast-parser": "1.8.5", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "node_modules/@yarnpkg/lockfile": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", - "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", - "dev": true - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "node_modules/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "dev": true, - "dependencies": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", - "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-node": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", - "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", - "dependencies": { - "acorn": "^7.0.0", - "acorn-walk": "^7.0.0", - "xtend": "^4.0.2" - } - }, - "node_modules/acorn-walk": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.1.1.tgz", - "integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/adm-zip": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.14.tgz", - "integrity": "sha512-/9aQCnQHF+0IiCl0qhXoK7qs//SwYE7zX8lsr/DNk1BRAHYxeLZPL4pguwK29gUEqasYQjqPtEpDRSWEkdHn9g==", - "engines": { - "node": ">=0.3.0" - } - }, - "node_modules/after": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", - "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", - "dev": true - }, - "node_modules/agent-base": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-1.0.2.tgz", - "integrity": "sha1-aJDT+yFwBLYrcPiSjg+uX4lSpwY=" - }, - "node_modules/agentkeepalive": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.1.0.tgz", - "integrity": "sha512-CW/n1wxF8RpEuuiq6Vbn9S8m0VSYDMnZESqaJ6F2cWN9fY8rei2qaxweIaRgq+ek8TqfoFIsUjaGNKGGEHElSg==", - "dev": true, - "dependencies": { - "debug": "^4.1.0", - "depd": "^1.1.2", - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", - "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", - "dev": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ajv": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", - "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "node_modules/ajv-errors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", - "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", - "dev": true - }, - "node_modules/ajv-keywords": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", - "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", - "dev": true - }, - "node_modules/align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "dependencies": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/alphanum-sort": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", - "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", - "dev": true - }, - "node_modules/alter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/alter/-/alter-0.2.0.tgz", - "integrity": "sha1-x1iICGF1cgNKrmJICvJrHU0cs80=", - "dependencies": { - "stable": "~0.1.3" - } - }, - "node_modules/amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "engines": { - "node": ">=0.4.2" - } - }, - "node_modules/angular-in-memory-web-api": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/angular-in-memory-web-api/-/angular-in-memory-web-api-0.10.0.tgz", - "integrity": "sha512-GzY4lHPR8suREYfgiYiJSBrgq0bdCiFw/LuqW5IsOQfRx98AvS69A5Ehz12oMLy9ABzoGpqmNC5fcFtClDAPqQ==" - }, - "node_modules/ansi-colors": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", - "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", - "dev": true, - "dependencies": { - "type-fest": "^0.11.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", - "dev": true, - "engines": [ - "node >= 0.8.0" - ], - "bin": { - "ansi-html": "bin/ansi-html" - } - }, - "node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/app-root-path": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.2.1.tgz", - "integrity": "sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA==", - "dev": true, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/append-transform": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", - "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", - "dev": true, - "dependencies": { - "default-require-extensions": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true - }, - "node_modules/archy": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/archy/-/archy-0.0.2.tgz", - "integrity": "sha1-kQ9Dv2YUH8M1VkWXq8GJ30Sz014=" - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/aria-query": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", - "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=", - "dev": true, - "dependencies": { - "ast-types-flow": "0.0.7", - "commander": "^2.11.0" - } - }, - "node_modules/arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", - "dev": true - }, - "node_modules/array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "dependencies": { - "array-uniq": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arraybuffer.slice": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", - "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", - "dev": true - }, - "node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", - "dev": true - }, - "node_modules/asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", - "dev": true, - "dependencies": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/assert": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", - "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", - "dev": true, - "dependencies": { - "object-assign": "^4.1.1", - "util": "0.10.3" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/assert/node_modules/inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - }, - "node_modules/assert/node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/assert/node_modules/util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "dependencies": { - "inherits": "2.0.1" - } - }, - "node_modules/assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ast-traverse": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ast-traverse/-/ast-traverse-0.1.1.tgz", - "integrity": "sha1-ac8rg4bxnc2hux4F1o/jWdiJfeY=" - }, - "node_modules/ast-types": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.3.tgz", - "integrity": "sha512-XTZ7xGML849LkQP86sWdQzfhwbt3YwIO6MqbX9mUNYY98VKaaVZP7YNNm70IpwecbkkxmfC5IYAzOQ/2p29zRA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/ast-types-flow": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", - "dev": true - }, - "node_modules/async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dev": true, - "dependencies": { - "lodash": "^4.17.14" - } - }, - "node_modules/async-each": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", - "dev": true - }, - "node_modules/async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "dev": true - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true, - "bin": { - "atob": "bin/atob.js" - }, - "engines": { - "node": ">= 4.5.0" - } - }, - "node_modules/autoprefixer": { - "version": "9.7.4", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.4.tgz", - "integrity": "sha512-g0Ya30YrMBAEZk60lp+qfX5YQllG+S5W3GYCFvyHTvhOki0AEQJLPEcIuGRsqVwLi8FvXPVtwTGhfr38hVpm0g==", - "dev": true, - "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" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/autoprefixer-core": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/autoprefixer-core/-/autoprefixer-core-3.1.2.tgz", - "integrity": "sha1-reXOni2dcbt//DHWlvpeh66+tjQ=", - "dependencies": { - "caniuse-db": "^1.0.30000006", - "postcss": "~2.2.5" - } - }, - "node_modules/autoprefixer/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/autoprefixer/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/autoprefixer/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", - "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", - "dev": true - }, - "node_modules/axobject-query": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz", - "integrity": "sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww==", - "dev": true, - "dependencies": { - "ast-types-flow": "0.0.7" - } - }, - "node_modules/babel-loader": { - "version": "8.0.6", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.6.tgz", - "integrity": "sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw==", - "dev": true, - "dependencies": { - "find-cache-dir": "^2.0.0", - "loader-utils": "^1.0.2", - "mkdirp": "^0.5.1", - "pify": "^4.0.1" - }, - "engines": { - "node": ">= 6.9" - } - }, - "node_modules/babel-loader/node_modules/find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/babel-loader/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/babel-loader/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/babel-loader/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/babel-plugin-dynamic-import-node": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", - "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", - "dev": true, - "dependencies": { - "object.assign": "^4.1.0" - } - }, - "node_modules/backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", - "dev": true - }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "node_modules/base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "dependencies": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base62": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/base62/-/base62-0.1.1.tgz", - "integrity": "sha1-e0F0wvlESXU7EcJlHAg9qEGnsIQ=", - "engines": { - "node": "*" - } - }, - "node_modules/base64-arraybuffer": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", - "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", - "dev": true, - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", - "dev": true - }, - "node_modules/base64id": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", - "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", - "dev": true - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/better-assert": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", - "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", - "dev": true, - "dependencies": { - "callsite": "1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/binary-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", - "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dev": true, - "optional": true, - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/bindings/node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "optional": true - }, - "node_modules/blob": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", - "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==", - "dev": true - }, - "node_modules/block-stream": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", - "dependencies": { - "inherits": "~2.0.0" - }, - "engines": { - "node": "0.4 || >=0.5.8" - } - }, - "node_modules/blocking-proxy": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz", - "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "blocking-proxy": "built/lib/bin.js" - }, - "engines": { - "node": ">=6.9.x" - } - }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "node_modules/bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true - }, - "node_modules/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "dev": true, - "dependencies": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/body-parser/node_modules/raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "dev": true, - "dependencies": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", - "dev": true, - "dependencies": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", - "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/breakable": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/breakable/-/breakable-1.0.0.tgz", - "integrity": "sha1-eEp5eRWjjq0nutRWtVcstLuqeME=" - }, - "node_modules/brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, - "node_modules/browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, - "dependencies": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "dev": true, - "dependencies": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" - } - }, - "node_modules/browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "dev": true, - "dependencies": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "node_modules/browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "dev": true, - "dependencies": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" - } - }, - "node_modules/browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", - "dev": true, - "dependencies": { - "bn.js": "^4.1.1", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.2", - "elliptic": "^6.0.0", - "inherits": "^2.0.1", - "parse-asn1": "^5.0.0" - } - }, - "node_modules/browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "dev": true, - "dependencies": { - "pako": "~1.0.5" - } - }, - "node_modules/browserslist": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.11.1.tgz", - "integrity": "sha512-DCTr3kDrKEYNw6Jb9HFxVLQNaue8z+0ZfRBRjmCunKDEXEBajKDj2Y+Uelg+Pi29OnvaSGwjOsnRyNEkXzHg5g==", - "dev": true, - "dependencies": { - "caniuse-lite": "^1.0.30001038", - "electron-to-chromium": "^1.3.390", - "node-releases": "^1.1.53", - "pkg-up": "^2.0.0" - }, - "bin": { - "browserslist": "cli.js" - } - }, - "node_modules/browserstack": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.3.tgz", - "integrity": "sha512-AO+mECXsW4QcqC9bxwM29O7qWa7bJT94uBFzeb5brylIQwawuEziwq20dPYbins95GlWzOawgyDNdjYAo32EKg==", - "dev": true, - "dependencies": { - "https-proxy-agent": "^2.2.1" - } - }, - "node_modules/browserstack/node_modules/agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "dependencies": { - "es6-promisify": "^5.0.0" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/browserstack/node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/browserstack/node_modules/https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "dev": true, - "dependencies": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - }, - "engines": { - "node": ">= 4.5.0" - } - }, - "node_modules/buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "dev": true, - "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "node_modules/buffer-alloc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", - "dev": true, - "dependencies": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" - } - }, - "node_modules/buffer-alloc-unsafe": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", - "dev": true - }, - "node_modules/buffer-fill": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", - "dev": true - }, - "node_modules/buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "node_modules/buffer-indexof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", - "dev": true - }, - "node_modules/buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true - }, - "node_modules/buffer/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "node_modules/builder-autoprefixer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/builder-autoprefixer/-/builder-autoprefixer-1.0.4.tgz", - "integrity": "sha1-nNjDdqbXoXIAvYyjMbUfIwDhrHA=", - "dependencies": { - "autoprefixer-core": "^3.1.1" - } - }, - "node_modules/builder-es6-module-to-cjs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/builder-es6-module-to-cjs/-/builder-es6-module-to-cjs-1.1.0.tgz", - "integrity": "sha1-efMpfRjEe7iLQ5R1OPoccnQnHuM=", - "dependencies": { - "es6-module-jstransform": "^0.1.2", - "is-module": "^1.0.0" - } - }, - "node_modules/builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", - "dev": true - }, - "node_modules/builtins": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", - "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=", - "dev": true - }, - "node_modules/bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", - "integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g=" - }, - "node_modules/cacache": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.0.tgz", - "integrity": "sha512-L0JpXHhplbJSiDGzyJJnJCTL7er7NzbBgxzVqLswEb4bO91Zbv17OUMuUeu/q0ZwKn3V+1HM4wb9tO4eVE/K8g==", - "dev": true, - "dependencies": { - "chownr": "^1.1.2", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "infer-owner": "^1.0.4", - "lru-cache": "^5.1.1", - "minipass": "^3.1.1", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", - "move-concurrently": "^1.0.1", - "p-map": "^3.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^2.7.1", - "ssri": "^8.0.0", - "tar": "^6.0.1", - "unique-filename": "^1.1.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/cacache/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "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" - }, - "engines": { - "node": "*" - } - }, - "node_modules/cacache/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/cacache/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/cacache/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cacache/node_modules/tar": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.0.1.tgz", - "integrity": "sha512-bKhKrrz2FJJj5s7wynxy/fyxpE0CmCjmOQ1KV4KkgXFWOgoIT/NbTMnB1n+LFNrNk0SSBVGGxcK5AGsyC+pW5Q==", - "dev": true, - "dependencies": { - "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" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/cacache/node_modules/tar/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/cacache/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "dependencies": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/caller-callsite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", - "dev": true, - "dependencies": { - "callsites": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/caller-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", - "dev": true, - "dependencies": { - "caller-callsite": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "dev": true, - "dependencies": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "node_modules/caniuse-db": { - "version": "1.0.30001038", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30001038.tgz", - "integrity": "sha512-yeQ2l99M9upOgMIRfZEdes6HuPbQiRZIMBumUwdXeEQz+faSXUZtZ8xeyEdU+TlJckH09M5NtM038sjKsRa2ow==" - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001038", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001038.tgz", - "integrity": "sha512-zii9quPo96XfOiRD4TrfYGs+QsGZpb2cGiMAzPjtf/hpFgB6zCPZgJb7I1+EATeMw/o+lG8FyRAnI+CWStHcaQ==", - "dev": true - }, - "node_modules/canonical-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-1.0.0.tgz", - "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==", - "dev": true - }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "node_modules/center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "dependencies": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chanel": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/chanel/-/chanel-2.2.0.tgz", - "integrity": "sha1-JlKVsouSqoIAX4Zd0VpIlMQ42Tw=", - "dependencies": { - "co": "*", - "generator-supported": "*", - "to-descriptor": "*" - } - }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "node_modules/chart.js": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.9.3.tgz", - "integrity": "sha512-+2jlOobSk52c1VU6fzkh3UwqHMdSlgH1xFv9FKMqHiNCpXsGPQa/+81AFa+i3jZ253Mq9aAycPwDjnn1XbRNNw==", - "dependencies": { - "chartjs-color": "^2.1.0", - "moment": "^2.10.2" - } - }, - "node_modules/chartjs-color": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.4.1.tgz", - "integrity": "sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==", - "dependencies": { - "chartjs-color-string": "^0.6.0", - "color-convert": "^1.9.3" - } - }, - "node_modules/chartjs-color-string": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz", - "integrity": "sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==", - "dependencies": { - "color-name": "^1.0.0" - } - }, - "node_modules/chokidar": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz", - "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==", - "dev": true, - "dependencies": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.3.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.1.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true - }, - "node_modules/chrome-trace-event": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", - "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", - "dev": true, - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/circular-dependency-plugin": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.0.tgz", - "integrity": "sha512-7p4Kn/gffhQaavNfyDFg7LS5S/UT1JAjyGd4UqR2+jzoYF02eDkj0Ec3+48TsIa4zghjLY87nQHIh/ecK9qLdw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "dependencies": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.2.0.tgz", - "integrity": "sha512-tgU3fKwzYjiLEQgPMD9Jt+JjHVL9kW93FiIMX/l7rivvOD4/LL0Mf7gda3+4U2KJBloybwgj5KEoQgGRioMiKQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "dev": true - }, - "node_modules/cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "dependencies": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" - } - }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/clone-deep/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/co": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/co/-/co-3.1.0.tgz", - "integrity": "sha1-TqVOpaCJOBUxheFSEMaNkJK8G3g=" - }, - "node_modules/coa": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", - "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", - "dev": true, - "dependencies": { - "@types/q": "^1.5.1", - "chalk": "^2.4.1", - "q": "^1.1.2" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/codelyzer": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-5.2.2.tgz", - "integrity": "sha512-jB4FZ1Sx7kZhvZVdf+N2BaKTdrrNZOL0Bj10RRfrhHrb3zEvXjJvvq298JPMJAiyiCS/v4zs1QlGU0ip7xGqeA==", - "dev": true, - "dependencies": { - "app-root-path": "^2.2.1", - "aria-query": "^3.0.0", - "axobject-query": "2.0.2", - "css-selector-tokenizer": "^0.7.1", - "cssauron": "^1.4.0", - "damerau-levenshtein": "^1.0.4", - "semver-dsl": "^1.0.1", - "source-map": "^0.5.7", - "sprintf-js": "^1.1.2" - } - }, - "node_modules/codelyzer/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/codelyzer/node_modules/sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", - "dev": true - }, - "node_modules/cogent": { - "resolved": "git+ssh://git@github.com/timaschew/cogent.git#2246bd071392f5053a3a110024fd608a40a593ba", - "dependencies": { - "debug": "*", - "generator-supported": "~0.0.1", - "netrc": "~0.1.3", - "proxy-agent": "^1.1.0", - "raw-body": "^1.1.2", - "statuses": "^1.0.2", - "write-to": "^1.0.0" - } - }, - "node_modules/collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "dependencies": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/color": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz", - "integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-convert/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-string": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", - "dev": true, - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "node_modules/commoner": { - "version": "0.10.8", - "resolved": "https://registry.npmjs.org/commoner/-/commoner-0.10.8.tgz", - "integrity": "sha1-NPw2cs0kOT6LtH5wyqApOBH08sU=", - "dependencies": { - "commander": "^2.5.0", - "detective": "^4.3.1", - "glob": "^5.0.15", - "graceful-fs": "^4.1.2", - "iconv-lite": "^0.4.5", - "mkdirp": "^0.5.0", - "private": "^0.1.6", - "q": "^1.1.2", - "recast": "^0.11.17" - }, - "bin": { - "commonize": "bin/commonize" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commoner/node_modules/ast-types": { - "version": "0.9.6", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.6.tgz", - "integrity": "sha1-ECyenpAF0+fjgpvwxPok7oYu6bk=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commoner/node_modules/esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/commoner/node_modules/glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", - "dependencies": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/commoner/node_modules/graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" - }, - "node_modules/commoner/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/commoner/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/commoner/node_modules/recast": { - "version": "0.11.23", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.11.23.tgz", - "integrity": "sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM=", - "dependencies": { - "ast-types": "0.9.6", - "esprima": "~3.1.0", - "private": "~0.1.5", - "source-map": "~0.5.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commoner/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/compare-versions": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", - "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", - "dev": true - }, - "node_modules/component": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/component/-/component-1.1.0.tgz", - "integrity": "sha1-NgSaold5i37lcUKWHhjgdnkYyao=", - "dependencies": { - "co": "^3.0.0", - "commander": "^2.2.0", - "component-build": "^1.2.2", - "component-consoler": "^2.0.0", - "component-flatten": "^1.0.1", - "component-ls": "^2.1.0", - "component-outdated2": "^1.0.4", - "component-pin": "^1.0.4", - "component-remotes": "^1.2.0", - "component-resolver": "^1.3.0", - "component-search2": "^1.1.1", - "component-updater": "^1.0.4", - "component-watcher": "^1.0.1", - "debug": "*", - "mkdirp": "~0.3.5", - "rimraf": "^2.2.6", - "semver": "^2.2.1", - "superagent": "~0.17.0", - "tiny-lr-fork": "0.0.5", - "win-fork": "~1.1.1" - }, - "bin": { - "component": "bin/component", - "component-build": "bin/component-build", - "component-crawl": "bin/component-crawl", - "component-duplicates": "bin/component-duplicates", - "component-help": "bin/component-help", - "component-install": "bin/component-install", - "component-link": "bin/component-link", - "component-ls": "bin/component-ls", - "component-outdated": "bin/component-outdated", - "component-pin": "bin/component-pin", - "component-search": "bin/component-search", - "component-update": "bin/component-update", - "component-validate": "bin/component-validate" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/component-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", - "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", - "dev": true - }, - "node_modules/component-build": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/component-build/-/component-build-1.2.2.tgz", - "integrity": "sha1-1bwl0lIE35On2aN6LeAE4RmkpXw=", - "dependencies": { - "builder-autoprefixer": "^1.0.3", - "builder-es6-module-to-cjs": "^1.1.0", - "component-builder": "^1.1.12", - "debug": "*" - } - }, - "node_modules/component-builder": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-builder/-/component-builder-1.2.1.tgz", - "integrity": "sha1-o5fBg3na9RH4Woy5TKDZIcHdsaQ=", - "dependencies": { - "requires": "^1.0.0", - "chanel": "^2.0.0", - "co": "^3.1.0", - "component-flatten": "^1.0.1", - "component-manifest": "^1.0.0", - "component-require2": "^1.0.1", - "cp": "^0.1.1", - "debug": "*", - "generator-supported": "~0.0.1", - "graceful-fs": "^2.0.3", - "mkdirp": "^0.3.5", - "syntax-error": "^1.1.1" - } - }, - "node_modules/component-consoler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/component-consoler/-/component-consoler-2.0.0.tgz", - "integrity": "sha1-Peq/BGwjm2EGNi7JaJQGNruhTCM=", - "dependencies": { - "debug": "*" - } - }, - "node_modules/component-downloader": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/component-downloader/-/component-downloader-1.2.0.tgz", - "integrity": "sha1-b/Z/9xyEaoWKrjGog1X5vxrUO20=", - "dependencies": { - "chanel": "^2.0.1", - "co": "^3", - "component-consoler": "^2.0.0", - "component-remotes": "^1.1.1", - "debug": "*", - "decompress": "~0.2.0", - "generator-supported": "~0.0.1", - "graceful-fs": "^2.0.2", - "mkdirp": "~0.3.5", - "rimraf": "^2.2.6", - "semver": "^2.2.0", - "unglob": "~0.1.2", - "write-to": "^1.0.0" - } - }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "node_modules/component-flatten": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/component-flatten/-/component-flatten-1.0.1.tgz", - "integrity": "sha1-W0n0msRcuIxDAyuXozb1P4fNbIw=", - "dependencies": { - "semver": "^2.2.1" - } - }, - "node_modules/component-inherit": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", - "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", - "dev": true - }, - "node_modules/component-ls": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/component-ls/-/component-ls-2.1.0.tgz", - "integrity": "sha1-wR7ez4mDbd6JBkrQT+JK3YClERI=", - "dependencies": { - "archy": "~0.0.2" - } - }, - "node_modules/component-manifest": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/component-manifest/-/component-manifest-1.0.0.tgz", - "integrity": "sha1-SNy7HqPGiHYifmra/PNxJbC81XI=", - "dependencies": { - "debug": "*", - "generator-supported": "~0.0.1", - "graceful-fs": "^2.0.1", - "unglob": "~0.1.1" - } - }, - "node_modules/component-outdated2": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/component-outdated2/-/component-outdated2-1.0.5.tgz", - "integrity": "sha1-IGbA9Cx1w18tS/3iv5/s4M5GPRQ=", - "dependencies": { - "component-consoler": "^2.0.0", - "component-flatten": "^1.0.1", - "component-remotes": "^1.1.5", - "generator-supported": "~0.0.1", - "semver": "^2.2.1" - } - }, - "node_modules/component-pin": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/component-pin/-/component-pin-1.0.5.tgz", - "integrity": "sha1-v6lOAepCB18ufU17X3i2Yt24NY0=", - "dependencies": { - "component-consoler": "^2.0.0", - "component-flatten": "^1.0.1", - "component-remotes": "^1.1.5", - "generator-supported": "~0.0.1", - "semver": "^2.2.1" - } - }, - "node_modules/component-remotes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/component-remotes/-/component-remotes-1.2.0.tgz", - "integrity": "sha1-PQeYpR5ziZxs5VnZN2Olb8eh2IM=", - "dependencies": { - "co": "^3", - "cogent": "git://github.com/timaschew/cogent.git#fix-redirects", - "component-consoler": "^2.0.0", - "component-validator": "^1.0.0", - "debug": "*", - "generator-supported": "~0.0.1", - "graceful-fs": "^2.0.2", - "semver": "^2.2.1" - } - }, - "node_modules/component-require2": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/component-require2/-/component-require2-1.1.1.tgz", - "integrity": "sha1-POrqGYc8HG74X1O48572koWvXMU=" - }, - "node_modules/component-resolver": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-resolver/-/component-resolver-1.3.0.tgz", - "integrity": "sha1-79grBa9krRJ4W5h8AfCsQfAKaNw=", - "dependencies": { - "chanel": "^2.0.2", - "co": "^3", - "component-consoler": "^2.0.0", - "component-downloader": "^1.2.0", - "component-flatten": "^1.0.1", - "component-remotes": "^1.2.0", - "component-validator": "^1.1.1", - "debug": "*", - "generator-supported": "~0.0.1", - "graceful-fs": "^2.0.1", - "semver": "^2.2.1", - "unglob": "~0.1.2" - } - }, - "node_modules/component-search2": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/component-search2/-/component-search2-1.1.1.tgz", - "integrity": "sha1-kmeDygb2Hvdd7YW7ZVuq3i/J65E=", - "dependencies": { - "bytes": "~0.3.0", - "cogent": "~0.4.0", - "component-consoler": "^2.0.0", - "debug": "*", - "event-stream": "^3.1.1", - "generator-supported": "~0.0.1", - "JSONStream": "~0.8.0", - "stream-to-array": "^1.0.0" - } - }, - "node_modules/component-search2/node_modules/bytes": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-0.3.0.tgz", - "integrity": "sha1-eOLg4ox/nHuYjqiu4NtNX6mUGTU=" - }, - "node_modules/component-search2/node_modules/cogent": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/cogent/-/cogent-0.4.3.tgz", - "integrity": "sha1-UAdE8R2D6cD8Q+9vlem5zHFaCFQ=", - "dependencies": { - "debug": "*", - "generator-supported": "~0.0.1", - "netrc": "~0.1.3", - "proxy-agent": "^1.1.0", - "raw-body": "^1.1.2", - "statuses": "^1.0.2", - "write-to": "^1.0.0" - } - }, - "node_modules/component-updater": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/component-updater/-/component-updater-1.0.5.tgz", - "integrity": "sha1-CHd/ZxzodeE+MHwdghfC6l8aSsQ=", - "dependencies": { - "component-consoler": "^2.0.0", - "component-flatten": "^1.0.1", - "component-remotes": "^1.1.5", - "generator-supported": "~0.0.1", - "semver": "^2.2.1" - } - }, - "node_modules/component-validator": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/component-validator/-/component-validator-1.1.1.tgz", - "integrity": "sha1-+lD+t/WL7IndaXktE6Mn4lJgjl4=", - "dependencies": { - "component-consoler": "^2.0.0" - } - }, - "node_modules/component-watcher": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/component-watcher/-/component-watcher-1.0.3.tgz", - "integrity": "sha1-wl6AF0sLQFVJXX7xJnmGJm2QxTU=", - "dependencies": { - "debug": "*", - "sane": "0" - } - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dev": true, - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dev": true, - "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compression/node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "engines": [ - "node >= 0.8" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/connect": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", - "dev": true, - "dependencies": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/connect/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/connect/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/console-browserify": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", - "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", - "dev": true - }, - "node_modules/constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true - }, - "node_modules/content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "dev": true, - "dependencies": { - "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.1" - } - }, - "node_modules/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", - "dev": true - }, - "node_modules/cookiejar": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-1.3.0.tgz", - "integrity": "sha1-3QCzVnkCHpnL1OhVua0EGRNHR2U=" - }, - "node_modules/copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", - "dev": true, - "dependencies": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" - } - }, - "node_modules/copy-concurrently/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/copy-webpack-plugin": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.1.1.tgz", - "integrity": "sha512-P15M5ZC8dyCjQHWwd4Ia/dm0SgVvZJMYeykVIVYXbGyqO4dWB5oyPHp9i7wjwo5LhtlhKbiBCdS2NvM07Wlybg==", - "dev": true, - "dependencies": { - "cacache": "^12.0.3", - "find-cache-dir": "^2.1.0", - "glob-parent": "^3.1.0", - "globby": "^7.1.1", - "is-glob": "^4.0.1", - "loader-utils": "^1.2.3", - "minimatch": "^3.0.4", - "normalize-path": "^3.0.0", - "p-limit": "^2.2.1", - "schema-utils": "^1.0.0", - "serialize-javascript": "^2.1.2", - "webpack-log": "^2.0.0" - }, - "engines": { - "node": ">= 6.9.0" - } - }, - "node_modules/copy-webpack-plugin/node_modules/cacache": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", - "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", - "dev": true, - "dependencies": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - } - }, - "node_modules/copy-webpack-plugin/node_modules/find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/copy-webpack-plugin/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "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" - }, - "engines": { - "node": "*" - } - }, - "node_modules/copy-webpack-plugin/node_modules/graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, - "node_modules/copy-webpack-plugin/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/copy-webpack-plugin/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/copy-webpack-plugin/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/copy-webpack-plugin/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/copy-webpack-plugin/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/copy-webpack-plugin/node_modules/p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/copy-webpack-plugin/node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/copy-webpack-plugin/node_modules/schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "dependencies": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/copy-webpack-plugin/node_modules/ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", - "dev": true, - "dependencies": { - "figgy-pudding": "^3.5.1" - } - }, - "node_modules/copy-webpack-plugin/node_modules/y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "node_modules/copy-webpack-plugin/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/core-js": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz", - "integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==", - "dev": true, - "hasInstallScript": true - }, - "node_modules/core-js-compat": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.4.tgz", - "integrity": "sha512-zAa3IZPvsJ0slViBQ2z+vgyyTuhd3MFn1rBQjZSKVEgB0UMYhUkCj9jJUVPgGTGqWvsBVmfnruXgTcNyTlEiSA==", - "dev": true, - "dependencies": { - "browserslist": "^4.8.3", - "semver": "7.0.0" - } - }, - "node_modules/core-js-compat/node_modules/semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "node_modules/cosmiconfig": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", - "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", - "dev": true, - "dependencies": { - "import-fresh": "^2.0.0", - "is-directory": "^0.3.1", - "js-yaml": "^3.13.1", - "parse-json": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cp": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/cp/-/cp-0.1.1.tgz", - "integrity": "sha1-OUanbBpT/+DmhZPzQcEkszbB8G0=" - }, - "node_modules/create-ecdh": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", - "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", - "dev": true, - "dependencies": { - "bn.js": "^4.1.0", - "elliptic": "^6.0.0" - } - }, - "node_modules/create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, - "dependencies": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "node_modules/create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, - "dependencies": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/cross-spawn/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, - "dependencies": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" - }, - "engines": { - "node": "*" - } - }, - "node_modules/css": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", - "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "source-map": "^0.6.1", - "source-map-resolve": "^0.5.2", - "urix": "^0.1.0" - } - }, - "node_modules/css-color-names": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", - "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/css-declaration-sorter": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", - "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", - "dev": true, - "dependencies": { - "postcss": "^7.0.1", - "timsort": "^0.3.0" - }, - "engines": { - "node": ">4" - } - }, - "node_modules/css-declaration-sorter/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/css-declaration-sorter/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/css-declaration-sorter/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/css-parse": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz", - "integrity": "sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q=", - "dev": true, - "dependencies": { - "css": "^2.0.0" - } - }, - "node_modules/css-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", - "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", - "dev": true, - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^3.2.1", - "domutils": "^1.7.0", - "nth-check": "^1.0.2" - } - }, - "node_modules/css-select-base-adapter": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", - "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", - "dev": true - }, - "node_modules/css-selector-tokenizer": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.2.tgz", - "integrity": "sha512-yj856NGuAymN6r8bn8/Jl46pR+OC3eEvAhfGYDUe7YPtTPAYrSSw4oAniZ9Y8T5B92hjhwTBLUen0/vKPxf6pw==", - "dev": true, - "dependencies": { - "cssesc": "^3.0.0", - "fastparse": "^1.1.2", - "regexpu-core": "^4.6.0" - } - }, - "node_modules/css-tree": { - "version": "1.0.0-alpha.37", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", - "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", - "dev": true, - "dependencies": { - "mdn-data": "2.0.4", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/css-tree/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/css-what": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.2.1.tgz", - "integrity": "sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/css/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cssauron": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz", - "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=", - "dev": true, - "dependencies": { - "through": "X.X.X" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cssnano": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz", - "integrity": "sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==", - "dev": true, - "dependencies": { - "cosmiconfig": "^5.0.0", - "cssnano-preset-default": "^4.0.7", - "is-resolvable": "^1.0.0", - "postcss": "^7.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/cssnano-preset-default": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz", - "integrity": "sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==", - "dev": true, - "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" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/cssnano-preset-default/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/cssnano-preset-default/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cssnano-preset-default/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/cssnano-util-get-arguments": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", - "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/cssnano-util-get-match": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", - "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/cssnano-util-raw-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", - "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", - "dev": true, - "dependencies": { - "postcss": "^7.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/cssnano-util-raw-cache/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/cssnano-util-raw-cache/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cssnano-util-raw-cache/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/cssnano-util-same-parent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", - "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/cssnano/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/cssnano/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cssnano/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/csso": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.0.3.tgz", - "integrity": "sha512-NL3spysxUkcrOgnpsT4Xdl2aiEiBG6bXswAABQVHcMrfjjBisFOKwLDOmf4wf32aPdcJws1zds2B0Rg+jqMyHQ==", - "dev": true, - "dependencies": { - "css-tree": "1.0.0-alpha.39" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/csso/node_modules/css-tree": { - "version": "1.0.0-alpha.39", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.39.tgz", - "integrity": "sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA==", - "dev": true, - "dependencies": { - "mdn-data": "2.0.6", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/csso/node_modules/mdn-data": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.6.tgz", - "integrity": "sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA==", - "dev": true - }, - "node_modules/csso/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/custom-event": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", - "dev": true - }, - "node_modules/cyclist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", - "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", - "dev": true - }, - "node_modules/damerau-levenshtein": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz", - "integrity": "sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug==", - "dev": true - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/data-uri-to-buffer": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-0.0.4.tgz", - "integrity": "sha1-RuE6udqOMJdFyNAc5UchPr2y/j8=" - }, - "node_modules/date-format": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz", - "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/debuglog": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", - "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/decompress": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/decompress/-/decompress-0.2.5.tgz", - "integrity": "sha1-0hMjPv4GbM2A2RTXk/GzDNmEuEc=", - "dependencies": { - "adm-zip": "^0.4.3", - "ext-name": "^1.0.0", - "get-stdin": "^0.1.0", - "mkdirp": "^0.3.5", - "nopt": "^2.2.0", - "rimraf": "^2.2.2", - "stream-combiner": "^0.0.4", - "tar": "^0.1.18", - "tempfile": "^0.1.2" - }, - "bin": { - "decompress": "cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/deep-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", - "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", - "dev": true, - "dependencies": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" - } - }, - "node_modules/deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" - }, - "node_modules/default-gateway": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", - "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", - "dev": true, - "dependencies": { - "execa": "^1.0.0", - "ip-regex": "^2.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/default-require-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", - "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", - "dev": true, - "dependencies": { - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", - "dev": true, - "dependencies": { - "clone": "^1.0.2" - } - }, - "node_modules/defaults/node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "dependencies": { - "object-keys": "^1.0.12" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/define-property/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/define-property/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/define-property/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/define-property/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" - }, - "node_modules/defs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/defs/-/defs-1.1.1.tgz", - "integrity": "sha1-siYJ8sehG6ej2xFoBcE5scr/qdI=", - "dependencies": { - "alter": "~0.2.0", - "ast-traverse": "~0.1.1", - "breakable": "~1.0.0", - "esprima-fb": "~15001.1001.0-dev-harmony-fb", - "simple-fmt": "~0.1.0", - "simple-is": "~0.2.0", - "stringmap": "~0.2.2", - "stringset": "~0.2.1", - "tryor": "~0.1.2", - "yargs": "~3.27.0" - }, - "bin": { - "defs": "build/es5/defs" - } - }, - "node_modules/defs/node_modules/esprima-fb": { - "version": "15001.1001.0-dev-harmony-fb", - "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz", - "integrity": "sha1-Q761fsJujPI3092LM+QlM1d/Jlk=", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/degenerator": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-1.0.4.tgz", - "integrity": "sha1-/PSQo37OJmRk2cxDGrmMWBnO0JU=", - "dependencies": { - "ast-types": "0.x.x", - "escodegen": "1.x.x", - "esprima": "3.x.x" - } - }, - "node_modules/degenerator/node_modules/esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/del": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", - "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", - "dev": true, - "dependencies": { - "@types/glob": "^7.1.1", - "globby": "^6.1.0", - "is-path-cwd": "^2.0.0", - "is-path-in-cwd": "^2.0.0", - "p-map": "^2.0.0", - "pify": "^4.0.1", - "rimraf": "^2.6.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/del/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "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" - }, - "engines": { - "node": "*" - } - }, - "node_modules/del/node_modules/globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "dev": true, - "dependencies": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/del/node_modules/globby/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/del/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/del/node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/del/node_modules/p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/dependency-graph": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.7.2.tgz", - "integrity": "sha512-KqtH4/EZdtdfWX0p6MGP9jljvxSY6msy/pRUD4jgNwVpv3v1QmNLlsB3LDSSUg79BRVSn7jI1QPRtArGABovAQ==", - "dev": true, - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/des.js": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", - "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", - "dev": true - }, - "node_modules/detect-node": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", - "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", - "dev": true - }, - "node_modules/detective": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/detective/-/detective-4.7.1.tgz", - "integrity": "sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig==", - "dependencies": { - "acorn": "^5.2.1", - "defined": "^1.0.0" - } - }, - "node_modules/detective/node_modules/acorn": { - "version": "5.7.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", - "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/dethroy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/dethroy/-/dethroy-1.0.2.tgz", - "integrity": "sha1-yCADMXaT3LzaP3UrAYLjvxbdRCE=" - }, - "node_modules/dezalgo": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", - "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=", - "dev": true, - "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "node_modules/di": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", - "dev": true - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "dev": true, - "dependencies": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" - } - }, - "node_modules/dir-glob": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", - "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", - "dev": true, - "dependencies": { - "path-type": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", - "dev": true - }, - "node_modules/dns-packet": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", - "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", - "dev": true, - "dependencies": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", - "dev": true, - "dependencies": { - "buffer-indexof": "^1.0.0" - } - }, - "node_modules/dom-serialize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", - "dev": true, - "dependencies": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" - } - }, - "node_modules/dom-serializer": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", - "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", - "dev": true, - "dependencies": { - "domelementtype": "^2.0.1", - "entities": "^2.0.0" - } - }, - "node_modules/dom-serializer/node_modules/domelementtype": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", - "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==", - "dev": true - }, - "node_modules/domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", - "dev": true, - "engines": { - "node": ">=0.4", - "npm": ">=1.2" - } - }, - "node_modules/domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", - "dev": true - }, - "node_modules/domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", - "dev": true, - "dependencies": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "node_modules/dot-prop": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", - "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", - "dev": true, - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/duplexer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", - "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=" - }, - "node_modules/duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - } - }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "dev": true - }, - "node_modules/electron-to-chromium": { - "version": "1.3.395", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.395.tgz", - "integrity": "sha512-kdn2cX6hZXDdz/O2Q8tZscITlsSv1a/7bOq/fQs7QAJ9iaRlnhZPccarNhxZv1tXgmgwCnKp/1lJNYLOG8Dxiw==", - "dev": true - }, - "node_modules/elliptic": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", - "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", - "dev": true, - "dependencies": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" - } - }, - "node_modules/emitter-component": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.0.0.tgz", - "integrity": "sha1-8E3Rj8PcPpp0y8DzELCIZm5MAW8=" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/encoding": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", - "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", - "dev": true, - "optional": true, - "dependencies": { - "iconv-lite": "~0.4.13" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/engine.io": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz", - "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==", - "dev": true, - "dependencies": { - "accepts": "~1.3.4", - "base64id": "1.0.0", - "cookie": "0.3.1", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.0", - "ws": "~3.3.1" - } - }, - "node_modules/engine.io-client": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", - "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==", - "dev": true, - "dependencies": { - "component-emitter": "1.2.1", - "component-inherit": "0.0.3", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.1", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "ws": "~3.3.1", - "xmlhttprequest-ssl": "~1.5.4", - "yeast": "0.1.2" - } - }, - "node_modules/engine.io-client/node_modules/component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "node_modules/engine.io-client/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/engine.io-client/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/engine.io-client/node_modules/ws": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", - "dev": true, - "dependencies": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" - } - }, - "node_modules/engine.io-parser": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz", - "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==", - "dev": true, - "dependencies": { - "after": "0.8.2", - "arraybuffer.slice": "~0.0.7", - "base64-arraybuffer": "0.1.5", - "blob": "0.0.5", - "has-binary2": "~1.0.2" - } - }, - "node_modules/engine.io/node_modules/cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/engine.io/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/engine.io/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/engine.io/node_modules/ws": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", - "dev": true, - "dependencies": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" - } - }, - "node_modules/enhanced-resolve": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz", - "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.5.0", - "tapable": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/enhanced-resolve/node_modules/graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, - "node_modules/ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", - "dev": true - }, - "node_modules/entities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", - "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==", - "dev": true - }, - "node_modules/err-code": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", - "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=", - "dev": true - }, - "node_modules/errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "dev": true, - "dependencies": { - "prr": "~1.0.1" - }, - "bin": { - "errno": "cli.js" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", - "dev": true, - "dependencies": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es6-module-jstransform": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/es6-module-jstransform/-/es6-module-jstransform-0.1.4.tgz", - "integrity": "sha1-OtHj6qt0UY9a8jwQFPGCjF6WfAI=", - "dependencies": { - "esprima-fb": "~3001.1.0-dev-harmony-fb", - "jstransform": "~3.0.0" - } - }, - "node_modules/es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", - "dev": true - }, - "node_modules/es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", - "dev": true, - "dependencies": { - "es6-promise": "^4.0.3" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/escodegen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz", - "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=4.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/escodegen/node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/escodegen/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/esprima-fb": { - "version": "3001.1.0-dev-harmony-fb", - "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-3001.0001.0000-dev-harmony-fb.tgz", - "integrity": "sha1-t303q8046gt3Qmu4vCkizmtCZBE=", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "dependencies": { - "estraverse": "^4.1.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/event-stream": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.5.tgz", - "integrity": "sha512-vyibDcu5JL20Me1fP734QBH/kenBGLZap2n0+XXM7mvuUPzJ20Ydqj1aKcIeMdri1p+PU+4yAKugjN8KCVst+g==", - "dependencies": { - "duplexer": "^0.1.1", - "from": "^0.1.7", - "map-stream": "0.0.7", - "pause-stream": "^0.0.11", - "split": "^1.0.1", - "stream-combiner": "^0.2.2", - "through": "^2.3.8" - } - }, - "node_modules/event-stream/node_modules/stream-combiner": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", - "integrity": "sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg=", - "dependencies": { - "duplexer": "~0.1.1", - "through": "~2.3.4" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", - "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==", - "dev": true - }, - "node_modules/events": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", - "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==", - "dev": true, - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/eventsource": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", - "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", - "dev": true, - "dependencies": { - "original": "^1.0.0" - }, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "dependencies": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "node_modules/execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "dependencies": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.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" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "dependencies": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/expand-brackets/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "dev": true, - "dependencies": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express/node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", - "dev": true - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/express/node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/ext-list": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-0.2.0.tgz", - "integrity": "sha1-NhTV8pn0pZKolinn3oJfF3TRmr0=", - "dependencies": { - "got": "^0.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ext-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-1.0.1.tgz", - "integrity": "sha1-GCgzVtxAo5NFXFRGDwWZzpfTDgw=", - "dependencies": { - "ext-list": "^0.2.0", - "underscore.string": "~2.3.3" - }, - "bin": { - "extname": "cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extend-shallow/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "dependencies": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true, - "engines": [ - "node >=0.6.0" - ] - }, - "node_modules/fast-deep-equal": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", - "dev": true - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" - }, - "node_modules/fastparse": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", - "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", - "dev": true - }, - "node_modules/faye-websocket": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.4.4.tgz", - "integrity": "sha1-wUxbO/FNdBf/v9mQwKdJXNnzN7w=", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/figgy-pudding": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", - "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", - "dev": true - }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/file-loader": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.0.0.tgz", - "integrity": "sha512-/aMOAYEFXDdjG0wytpTL5YQLfZnnTmLNjn+AIrJ/6HVnTfDqLsVKUUwkDf4I4kgex36BvjuXEn/TX9B/1ESyqQ==", - "dev": true, - "dependencies": { - "loader-utils": "^2.0.0", - "schema-utils": "^2.6.5" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/file-uri-to-path": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-0.0.2.tgz", - "integrity": "sha1-N83RtbkFQEs/BeGyNkW+aU/3D4I=" - }, - "node_modules/fileset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", - "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", - "dev": true, - "dependencies": { - "glob": "^7.0.3", - "minimatch": "^3.0.3" - } - }, - "node_modules/fileset/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "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" - }, - "engines": { - "node": "*" - } - }, - "node_modules/fileset/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dev": true, - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/find-cache-dir": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", - "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", - "dev": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-cache-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-cache-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-cache-dir/node_modules/make-dir": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", - "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-cache-dir/node_modules/p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/find-cache-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-cache-dir/node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/find-cache-dir/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-cache-dir/node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-cache-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", - "dev": true - }, - "node_modules/flush-write-stream": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "readable-stream": "^2.3.6" - } - }, - "node_modules/follow-redirects": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.11.0.tgz", - "integrity": "sha512-KZm0V+ll8PfBrKwMzdo5D13b1bur9Iq9Zd/RMmAoQQcl2PxxFml8cxXPaaPYVbV0RjNjq1CU7zIzAOqtUPudmA==", - "dev": true, - "dependencies": { - "debug": "^3.0.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/follow-redirects/node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/formidable": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.14.tgz", - "integrity": "sha1-Kz9MQRy7X91pXESEPiojUUpDIxo=", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "dependencies": { - "map-cache": "^0.2.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=" - }, - "node_modules/from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "node_modules/fs-extra": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.2.tgz", - "integrity": "sha1-+RcExT0bRh+JNFKwwwfZmXZHq2s=", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "node_modules/fs-extra/node_modules/graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" - } - }, - "node_modules/fs-write-stream-atomic/node_modules/graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "node_modules/fsevents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", - "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/fstream": { - "version": "0.1.31", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-0.1.31.tgz", - "integrity": "sha1-czfwWPu7vvqMn1YaKMqwhJICyYg=", - "dependencies": { - "graceful-fs": "~3.0.2", - "inherits": "~2.0.0", - "mkdirp": "0.5", - "rimraf": "2" - }, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/fstream/node_modules/graceful-fs": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.12.tgz", - "integrity": "sha512-J55gaCS4iTTJfTXIxSVw3EMQckcqkpdRv3IR7gu6sq0+tbC363Zx6KH/SEwXASK9JRbhyZmVjJEVJIOxYsB3Qg==", - "dependencies": { - "natives": "^1.1.3" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/fstream/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/ftp": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", - "integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=", - "dependencies": { - "readable-stream": "1.1.x", - "xregexp": "2.0.0" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/ftp/node_modules/readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/generator-supported": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/generator-supported/-/generator-supported-0.0.1.tgz", - "integrity": "sha1-kivSIBpsONj6y4FdWT2KA8iYwpk=" - }, - "node_modules/gensync": { - "version": "1.0.0-beta.1", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "dev": true - }, - "node_modules/get-stdin": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-0.1.0.tgz", - "integrity": "sha1-WZivJKr8gC0VyCxoVlfuuLENSpE=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/get-uri": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-0.1.4.tgz", - "integrity": "sha1-NfinlUwSn7Ey/y3fXtgaV8uKnlQ=", - "dependencies": { - "data-uri-to-buffer": "0", - "debug": "2", - "extend": "3", - "file-uri-to-path": "0", - "ftp": "~0.3.5", - "readable-stream": "2" - } - }, - "node_modules/get-uri/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/get-uri/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0" - } - }, - "node_modules/glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", - "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", - "dependencies": { - "inherits": "2", - "minimatch": "0.3" - }, - "engines": { - "node": "*" - } - }, - "node_modules/glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "dependencies": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - } - }, - "node_modules/glob-parent/node_modules/is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", - "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", - "dependencies": { - "lru-cache": "2", - "sigmund": "~1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/globby": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", - "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", - "dev": true, - "dependencies": { - "array-union": "^1.0.1", - "dir-glob": "^2.0.0", - "glob": "^7.1.2", - "ignore": "^3.3.5", - "pify": "^3.0.0", - "slash": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/globby/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "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" - }, - "engines": { - "node": "*" - } - }, - "node_modules/globby/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/globby/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/got": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/got/-/got-0.2.0.tgz", - "integrity": "sha1-0Awkiyn9zK6pQN+coJlev/MbUaU=", - "dependencies": { - "object-assign": "^0.3.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/graceful-fs": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz", - "integrity": "sha1-fNLNsiiko/Nule+mzBQt59GhNtA=", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", - "dev": true - }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "dev": true, - "dependencies": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-binary2": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", - "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", - "dev": true, - "dependencies": { - "isarray": "2.0.1" - } - }, - "node_modules/has-binary2/node_modules/isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", - "dev": true - }, - "node_modules/has-cors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", - "dev": true - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "dependencies": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "dependencies": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values/node_modules/kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "node_modules/hex-color-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", - "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", - "dev": true - }, - "node_modules/hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "dependencies": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/hosted-git-info": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.4.tgz", - "integrity": "sha512-4oT62d2jwSDBbLLFLZE+1vPuQ1h8p9wjrJ8Mqx5TjsyWmBMV5B13eJqn8pvluqubLf3cJPTfiYCIwNwDNmzScQ==", - "dev": true, - "dependencies": { - "lru-cache": "^5.1.1" - } - }, - "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/hosted-git-info/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "node_modules/hsl-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", - "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=", - "dev": true - }, - "node_modules/hsla-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", - "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=", - "dev": true - }, - "node_modules/html-comment-regex": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", - "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", - "dev": true - }, - "node_modules/html-entities": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", - "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", - "dev": true, - "engines": [ - "node >= 0.4.0" - ] - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", - "dev": true - }, - "node_modules/http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", - "dev": true - }, - "node_modules/http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "dev": true, - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/http-errors/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "node_modules/http-parser-js": { - "version": "0.4.10", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz", - "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=", - "dev": true - }, - "node_modules/http-proxy": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz", - "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==", - "dev": true, - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/http-proxy-agent": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-0.2.7.tgz", - "integrity": "sha1-4X/aZfCQLZUs55IeYsf/iGJlWl4=", - "dependencies": { - "agent-base": "~1.0.1", - "debug": "2", - "extend": "3" - } - }, - "node_modules/http-proxy-agent/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/http-proxy-agent/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/http-proxy-middleware": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", - "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", - "dev": true, - "dependencies": { - "http-proxy": "^1.17.0", - "is-glob": "^4.0.0", - "lodash": "^4.17.11", - "micromatch": "^3.1.10" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", - "dev": true - }, - "node_modules/https-proxy-agent": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-0.3.6.tgz", - "integrity": "sha1-cT+jjl01P1DrFKNC/r4pAz7RYZs=", - "dependencies": { - "agent-base": "~1.0.1", - "debug": "2", - "extend": "3" - } - }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", - "dev": true, - "dependencies": { - "ms": "^2.0.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "dev": true - }, - "node_modules/iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", - "dev": true - }, - "node_modules/ignore": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", - "dev": true - }, - "node_modules/ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "dev": true, - "dependencies": { - "minimatch": "^3.0.4" - } - }, - "node_modules/ignore-walk/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/image-size": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", - "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", - "dev": true, - "optional": true, - "bin": { - "image-size": "bin/image-size.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", - "dev": true - }, - "node_modules/import-cwd": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", - "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", - "dev": true, - "dependencies": { - "import-from": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/import-fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", - "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", - "dev": true, - "dependencies": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/import-from": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", - "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", - "dev": true, - "dependencies": { - "resolve-from": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", - "dev": true, - "dependencies": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/indexes-of": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", - "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", - "dev": true - }, - "node_modules/indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true - }, - "node_modules/infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "dev": true - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/inquirer": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz", - "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==", - "dev": true, - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^3.0.0", - "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.4.0", - "rxjs": "^6.5.3", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/inquirer/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "dependencies": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/inquirer/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/internal-ip": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", - "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", - "dev": true, - "dependencies": { - "default-gateway": "^4.2.0", - "ipaddr.js": "^1.9.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "dependencies": { - "loose-envify": "^1.0.0" - } - }, - "node_modules/invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" - }, - "node_modules/ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-absolute-url": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", - "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-arguments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", - "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "node_modules/is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-color-stop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", - "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", - "dev": true, - "dependencies": { - "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" - } - }, - "node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-descriptor/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-docker": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz", - "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=", - "dev": true - }, - "node_modules/is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=" - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-path-in-cwd": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", - "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", - "dev": true, - "dependencies": { - "is-path-inside": "^2.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-path-inside": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", - "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", - "dev": true, - "dependencies": { - "path-is-inside": "^1.0.2" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", - "dev": true - }, - "node_modules/is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true - }, - "node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-svg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", - "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", - "dev": true, - "dependencies": { - "html-comment-regex": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-wsl": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.1.1.tgz", - "integrity": "sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "node_modules/isbinaryfile": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", - "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", - "dev": true, - "dependencies": { - "buffer-alloc": "^1.2.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "node_modules/istanbul-api": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.6.tgz", - "integrity": "sha512-x0Eicp6KsShG1k1rMgBAi/1GgY7kFGEBwQpw3PXGEmu+rBcBNhqU8g2DgY9mlepAsLPzrzrbqSgCGANnki4POA==", - "dev": true, - "dependencies": { - "async": "^2.6.2", - "compare-versions": "^3.4.0", - "fileset": "^2.0.3", - "istanbul-lib-coverage": "^2.0.5", - "istanbul-lib-hook": "^2.0.7", - "istanbul-lib-instrument": "^3.3.0", - "istanbul-lib-report": "^2.0.8", - "istanbul-lib-source-maps": "^3.0.6", - "istanbul-reports": "^2.2.4", - "js-yaml": "^3.13.1", - "make-dir": "^2.1.0", - "minimatch": "^3.0.4", - "once": "^1.4.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-api/node_modules/istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-api/node_modules/istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", - "dev": true, - "dependencies": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-api/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/istanbul-api/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-hook": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", - "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", - "dev": true, - "dependencies": { - "append-transform": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz", - "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.7.5", - "@babel/parser": "^7.7.5", - "@babel/template": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/istanbul-lib-report": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", - "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-report/node_modules/istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul-reports": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", - "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jasmine": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz", - "integrity": "sha1-awicChFXax8W3xG4AUbZHU6Lij4=", - "dev": true, - "dependencies": { - "exit": "^0.1.2", - "glob": "^7.0.6", - "jasmine-core": "~2.8.0" - }, - "bin": { - "jasmine": "bin/jasmine.js" - } - }, - "node_modules/jasmine-core": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.5.0.tgz", - "integrity": "sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==", - "dev": true - }, - "node_modules/jasmine-spec-reporter": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz", - "integrity": "sha512-FZBoZu7VE5nR7Nilzy+Np8KuVIOxF4oXDPDknehCYBDE080EnlPu0afdZNmpGDBRCUBv3mj5qgqCRmk6W/K8vg==", - "dev": true, - "dependencies": { - "colors": "1.1.2" - } - }, - "node_modules/jasmine/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "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" - }, - "engines": { - "node": "*" - } - }, - "node_modules/jasmine/node_modules/jasmine-core": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", - "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=", - "dev": true - }, - "node_modules/jasmine/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/jasminewd2": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz", - "integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=", - "dev": true, - "engines": { - "node": ">= 6.9.x" - } - }, - "node_modules/jest-worker": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", - "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", - "dev": true, - "dependencies": { - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">= 8.3" - } - }, - "node_modules/jest-worker/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/js-base64": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", - "integrity": "sha1-8OgK4DmkvWVLXygfyT8EqRSn/M4=" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/js-yaml/node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.2.0.tgz", - "integrity": "sha512-2tLgY7LRNZ9Hd6gmCuBG5/OjRHQpSgJQqJoYyLLOhUgn8LdOYrjaZLcxkWnDads+AD/haWWioPNziXQcgvQJ/g==", - "dev": true - }, - "node_modules/json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "node_modules/json3": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", - "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==", - "dev": true - }, - "node_modules/json5": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.2.tgz", - "integrity": "sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.6" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonfile/node_modules/graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true, - "optional": true - }, - "node_modules/jsonparse": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz", - "integrity": "sha1-MwVCrT8KZUZlt3jz6y2an6UHrGQ=", - "engines": [ - "node >= 0.2.0" - ] - }, - "node_modules/JSONStream": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.8.4.tgz", - "integrity": "sha1-kWV9/m/4V0gwZhMrRhi2Lo9Ih70=", - "dependencies": { - "jsonparse": "0.0.5", - "through": ">=2.2.7 <3" - }, - "bin": { - "JSONStream": "index.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "node_modules/jstransform": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jstransform/-/jstransform-3.0.0.tgz", - "integrity": "sha1-olkats7o2XvzvoMNv6IxO4fNZAs=", - "dependencies": { - "base62": "0.1.1", - "esprima-fb": "~3001.1.0-dev-harmony-fb", - "source-map": "0.1.31" - }, - "engines": { - "node": ">=0.8.8" - } - }, - "node_modules/jstransform/node_modules/source-map": { - "version": "0.1.31", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.31.tgz", - "integrity": "sha1-n3BNDWnZ4TioG63267T94z0VHGE=", - "dependencies": { - "amdefine": ">=0.0.4" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/jszip": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.3.0.tgz", - "integrity": "sha512-EJ9k766htB1ZWnsV5ZMDkKLgA+201r/ouFF8R2OigVjVdcm2rurcBrrdXaeqBJbqnUVMko512PYmlncBKE1Huw==", - "dev": true, - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "set-immediate-shim": "~1.0.1" - } - }, - "node_modules/karma": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/karma/-/karma-4.3.0.tgz", - "integrity": "sha512-NSPViHOt+RW38oJklvYxQC4BSQsv737oQlr/r06pCM+slDOr4myuI1ivkRmp+3dVpJDfZt2DmaPJ2wkx+ZZuMQ==", - "dev": true, - "dependencies": { - "bluebird": "^3.3.0", - "body-parser": "^1.16.1", - "braces": "^3.0.2", - "chokidar": "^3.0.0", - "colors": "^1.1.0", - "connect": "^3.6.0", - "core-js": "^3.1.3", - "di": "^0.0.1", - "dom-serialize": "^2.2.0", - "flatted": "^2.0.0", - "glob": "^7.1.1", - "graceful-fs": "^4.1.2", - "http-proxy": "^1.13.0", - "isbinaryfile": "^3.0.0", - "lodash": "^4.17.14", - "log4js": "^4.0.0", - "mime": "^2.3.1", - "minimatch": "^3.0.2", - "optimist": "^0.6.1", - "qjobs": "^1.1.4", - "range-parser": "^1.2.0", - "rimraf": "^2.6.0", - "safe-buffer": "^5.0.1", - "socket.io": "2.1.1", - "source-map": "^0.6.1", - "tmp": "0.0.33", - "useragent": "2.3.0" - }, - "bin": { - "karma": "bin/karma" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/karma-chrome-launcher": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz", - "integrity": "sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg==", - "dev": true, - "dependencies": { - "which": "^1.2.1" - } - }, - "node_modules/karma-coverage-istanbul-reporter": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-2.1.1.tgz", - "integrity": "sha512-CH8lTi8+kKXGvrhy94+EkEMldLCiUA0xMOiL31vvli9qK0T+qcXJAwWBRVJWnVWxYkTmyWar8lPz63dxX6/z1A==", - "dev": true, - "dependencies": { - "istanbul-api": "^2.1.6", - "minimatch": "^3.0.4" - } - }, - "node_modules/karma-coverage-istanbul-reporter/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/karma-jasmine": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-2.0.1.tgz", - "integrity": "sha512-iuC0hmr9b+SNn1DaUD2QEYtUxkS1J+bSJSn7ejdEexs7P8EYvA1CWkEdrDQ+8jVH3AgWlCNwjYsT1chjcNW9lA==", - "dev": true, - "dependencies": { - "jasmine-core": "^3.3" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/karma-jasmine-html-reporter": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.5.3.tgz", - "integrity": "sha512-ci0VrjuCaFj+9d1tYlTE3KIPUCp0rz874zWWU3JgCMqGIyw5ke+BXWFPOAGAqUdCJcrMwneyvp1zFXA74MiPUA==", - "dev": true - }, - "node_modules/karma-source-map-support": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", - "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", - "dev": true, - "dependencies": { - "source-map-support": "^0.5.5" - } - }, - "node_modules/karma/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "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" - }, - "engines": { - "node": "*" - } - }, - "node_modules/karma/node_modules/graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, - "node_modules/karma/node_modules/mime": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", - "dev": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/karma/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/karma/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/killable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", - "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", - "dev": true - }, - "node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "dependencies": { - "invert-kv": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/less": { - "version": "3.11.1", - "resolved": "https://registry.npmjs.org/less/-/less-3.11.1.tgz", - "integrity": "sha512-tlWX341RECuTOvoDIvtFqXsKj072hm3+9ymRBe76/mD6O5ZZecnlAOVDlWAleF2+aohFrxNidXhv2773f6kY7g==", - "dev": true, - "dependencies": { - "clone": "^2.1.2", - "errno": "^0.1.1", - "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "mime": "^1.4.1", - "mkdirp": "^0.5.0", - "promise": "^7.1.1", - "request": "^2.83.0", - "source-map": "~0.6.0", - "tslib": "^1.10.0" - }, - "bin": { - "lessc": "bin/lessc" - }, - "engines": { - "node": ">=6" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "mime": "^1.4.1", - "mkdirp": "^0.5.0", - "promise": "^7.1.1", - "source-map": "~0.6.0" - } - }, - "node_modules/less-loader": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-5.0.0.tgz", - "integrity": "sha512-bquCU89mO/yWLaUq0Clk7qCsKhsF/TZpJUzETRvJa9KSVEL9SO3ovCvdEHISBhrC81OwC8QSVX7E0bzElZj9cg==", - "dev": true, - "dependencies": { - "clone": "^2.1.1", - "loader-utils": "^1.1.0", - "pify": "^4.0.1" - }, - "engines": { - "node": ">= 4.8.0" - } - }, - "node_modules/less-loader/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/less-loader/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/less/node_modules/graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true, - "optional": true - }, - "node_modules/less/node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, - "optional": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/less/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "optional": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/less/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/levenary": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", - "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", - "dev": true, - "dependencies": { - "leven": "^3.1.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/license-webpack-plugin": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-2.1.4.tgz", - "integrity": "sha512-1Xq72fmPbTg5KofXs+yI5L4QqPFjQ6mZxoeI6D7gfiEDOtaEIk6PGrdLaej90bpDqKNHNxlQ/MW4tMAL6xMPJQ==", - "dev": true, - "dependencies": { - "@types/webpack-sources": "^0.1.5", - "webpack-sources": "^1.2.0" - } - }, - "node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "dev": true, - "dependencies": { - "immediate": "~3.0.5" - } - }, - "node_modules/loader-runner": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", - "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", - "dev": true, - "engines": { - "node": ">=4.3.0 <5.0.0 || >=5.10" - } - }, - "node_modules/loader-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", - "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", - "dev": true - }, - "node_modules/lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", - "dev": true - }, - "node_modules/log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/log4js": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-4.5.1.tgz", - "integrity": "sha512-EEEgFcE9bLgaYUKuozyFfytQM2wDHtXn4tAN41pkaxpNjAykv11GVdeI4tHtmPWW4Xrgh9R/2d7XYghDVjbKKw==", - "dev": true, - "dependencies": { - "date-format": "^2.0.0", - "debug": "^4.1.1", - "flatted": "^2.0.0", - "rfdc": "^1.1.4", - "streamroller": "^1.0.6" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/loglevel": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.7.tgz", - "integrity": "sha512-cY2eLFrQSAfVPhCgH1s7JI73tMbg9YC3v3+ZHVW67sBS7UxWzNEk/ZBbSfLykBWHp33dqqtOv82gjhKEi81T/A==", - "dev": true, - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lru-cache": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", - "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" - }, - "node_modules/magic-string": { - "version": "0.25.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", - "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", - "dev": true, - "dependencies": { - "sourcemap-codec": "^1.4.4" - } - }, - "node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/make-fetch-happen": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-8.0.4.tgz", - "integrity": "sha512-hIFoqGq1db0QMiy/Atr/pI1Rs4rDV+ZdGSey2SQyF3KK3u1z4aj9mS5UdNnZkdQpA+H3pGn0J3KlEwsi2x4EqA==", - "dev": true, - "dependencies": { - "agentkeepalive": "^4.1.0", - "cacache": "^15.0.0", - "http-cache-semantics": "^4.0.4", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^5.1.1", - "minipass": "^3.0.0", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^1.1.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "promise-retry": "^1.1.1", - "socks-proxy-agent": "^5.0.0", - "ssri": "^8.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/make-fetch-happen/node_modules/agent-base": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.0.tgz", - "integrity": "sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==", - "dev": true, - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/make-fetch-happen/node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/make-fetch-happen/node_modules/https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "dev": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/make-fetch-happen/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/make-fetch-happen/node_modules/smart-buffer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", - "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==", - "dev": true, - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/make-fetch-happen/node_modules/socks": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.3.tgz", - "integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==", - "dev": true, - "dependencies": { - "ip": "1.1.5", - "smart-buffer": "^4.1.0" - }, - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/make-fetch-happen/node_modules/socks-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.0.tgz", - "integrity": "sha512-lEpa1zsWCChxiynk+lCycKuC502RxDWLKJZoIhnxrWNjLSDGYRFflHA1/228VkRcnv9TIb8w98derGbpKxJRgA==", - "dev": true, - "dependencies": { - "agent-base": "6", - "debug": "4", - "socks": "^2.3.3" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/make-fetch-happen/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/makeerror": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", - "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", - "dependencies": { - "tmpl": "1.0.x" - } - }, - "node_modules/mamacro": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", - "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==", - "dev": true - }, - "node_modules/map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, - "dependencies": { - "p-defer": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/map-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", - "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=" - }, - "node_modules/map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "dependencies": { - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "node_modules/mdn-data": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", - "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", - "dev": true - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "dependencies": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/memory-fs": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", - "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", - "dev": true, - "dependencies": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - }, - "engines": { - "node": ">=4.3.0 <5.0.0 || >=5.10" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", - "dev": true - }, - "node_modules/merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "dev": true, - "dependencies": { - "source-map": "^0.6.1" - } - }, - "node_modules/merge-source-map/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/methods": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/methods/-/methods-0.0.1.tgz", - "integrity": "sha1-J3yQ+L7zlwlkWoNxxRw7bGSOBow=" - }, - "node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, - "dependencies": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - }, - "bin": { - "miller-rabin": "bin/miller-rabin" - } - }, - "node_modules/mime": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.5.tgz", - "integrity": "sha1-nu0HMCKov14WyFZsaGe4gyv7+hM=" - }, - "node_modules/mime-db": { - "version": "1.43.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", - "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.26", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", - "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", - "dev": true, - "dependencies": { - "mime-db": "1.43.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/mini-css-extract-plugin": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz", - "integrity": "sha512-lp3GeY7ygcgAmVIcRPBVhIkf8Us7FZjA+ILpal44qLdSu11wmjKQ3d9k15lfD7pO4esu9eUIAW7qiYIBppv40A==", - "dev": true, - "dependencies": { - "loader-utils": "^1.1.0", - "normalize-url": "1.9.1", - "schema-utils": "^1.0.0", - "webpack-sources": "^1.1.0" - }, - "engines": { - "node": ">= 6.9.0" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/normalize-url": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", - "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", - "dev": true, - "dependencies": { - "object-assign": "^4.0.1", - "prepend-http": "^1.0.0", - "query-string": "^4.1.0", - "sort-keys": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "dependencies": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "node_modules/minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, - "node_modules/minimatch": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", - "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", - "dependencies": { - "lru-cache": "2", - "sigmund": "~1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "node_modules/minipass": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.1.tgz", - "integrity": "sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-fetch": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.2.1.tgz", - "integrity": "sha512-ssHt0dkljEDaKmTgQ04DQgx2ag6G2gMPxA5hpcsoeTbfDgRf2fC2gNSRc6kISjD7ckCpHwwQvXxuTBK8402fXg==", - "dev": true, - "dependencies": { - "encoding": "^0.1.12", - "minipass": "^3.1.0", - "minipass-pipeline": "^1.2.2", - "minipass-sized": "^1.0.3", - "minizlib": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "optionalDependencies": { - "encoding": "^0.1.12" - } - }, - "node_modules/minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-json-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz", - "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==", - "dev": true, - "dependencies": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" - } - }, - "node_modules/minipass-json-stream/node_modules/jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", - "dev": true, - "engines": [ - "node >= 0.2.0" - ] - }, - "node_modules/minipass-pipeline": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.2.tgz", - "integrity": "sha512-3JS5A2DKhD2g0Gg8x3yamO0pj7YeKGwVlDS90pF++kxptwx/F+B//roxf9SqYil5tQo65bijy+dAuAFZmYOouA==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.0.tgz", - "integrity": "sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/mississippi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", - "dev": true, - "dependencies": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "dependencies": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mixin-deep/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mkdirp": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", - "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=" - }, - "node_modules/moment": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==", - "engines": { - "node": "*" - } - }, - "node_modules/move-concurrently": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", - "dev": true, - "dependencies": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" - } - }, - "node_modules/move-concurrently/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", - "dev": true, - "dependencies": { - "dns-packet": "^1.3.1", - "thunky": "^1.0.2" - }, - "bin": { - "multicast-dns": "cli.js" - } - }, - "node_modules/multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", - "dev": true - }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "node_modules/nan": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", - "dev": true, - "optional": true - }, - "node_modules/nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nanomatch/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/natives": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.6.tgz", - "integrity": "sha512-6+TDFewD4yxY14ptjKaS63GVdtKiES1pTPyxn9Jb0rBqPMZ7VcCiooEhPNsr+mqHtMGxa/5c/HhcC4uPEUw/nA==" - }, - "node_modules/negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", - "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", - "dev": true - }, - "node_modules/netmask": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-1.0.6.tgz", - "integrity": "sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU=", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/netrc": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/netrc/-/netrc-0.1.4.tgz", - "integrity": "sha1-a+lPysqNd63gqWcNxGCRTJRHJEQ=" - }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node_modules/node-forge": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz", - "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==", - "dev": true, - "engines": { - "node": ">= 4.5.0" - } - }, - "node_modules/node-libs-browser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", - "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", - "dev": true, - "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_modules/node-libs-browser/node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, - "node_modules/node-libs-browser/node_modules/safe-buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", - "dev": true - }, - "node_modules/node-libs-browser/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/node-releases": { - "version": "1.1.53", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.53.tgz", - "integrity": "sha512-wp8zyQVwef2hpZ/dJH7SfSrIPD6YoJz6BDQDpGEkcA0s3LpAQoxBIYmfIq6QAhC1DhwsyCgTaTTcONwX8qzCuQ==", - "dev": true - }, - "node_modules/nopt": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.2.1.tgz", - "integrity": "sha1-KqCbfRdoSHs7ianFqlIzW/8Lrqc=", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - } - }, - "node_modules/noptify": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/noptify/-/noptify-0.0.3.tgz", - "integrity": "sha1-WPZUpz2XU98MUdlobckhBKZ/S7s=", - "dependencies": { - "nopt": "~2.0.0" - } - }, - "node_modules/noptify/node_modules/nopt": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.0.0.tgz", - "integrity": "sha1-ynQW8gpeP5w7hhgPlilfo9C1Lg0=", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - } - }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/normalize-package-data/node_modules/hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-url": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", - "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/npm-bundled": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", - "dev": true, - "dependencies": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "node_modules/npm-install-checks": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-4.0.0.tgz", - "integrity": "sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==", - "dev": true, - "dependencies": { - "semver": "^7.1.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm-install-checks/node_modules/semver": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", - "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", - "dev": true - }, - "node_modules/npm-package-arg": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.0.1.tgz", - "integrity": "sha512-/h5Fm6a/exByzFSTm7jAyHbgOqErl9qSNJDQF32Si/ZzgwT2TERVxRxn3Jurw1wflgyVVAxnFR4fRHPM7y1ClQ==", - "dev": true, - "dependencies": { - "hosted-git-info": "^3.0.2", - "semver": "^7.0.0", - "validate-npm-package-name": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm-package-arg/node_modules/semver": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", - "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm-packlist": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-2.1.1.tgz", - "integrity": "sha512-95TSDvGwujIhqfSpIiRRLodEF+y6mJMopuZdahoGzqtRDFZXGav46S0p6ngeWaiAkb5R72w6eVARhzej0HvZeQ==", - "dev": true, - "dependencies": { - "glob": "^7.1.6", - "ignore-walk": "^3.0.3", - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" - }, - "bin": { - "npm-packlist": "bin/index.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm-packlist/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "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" - }, - "engines": { - "node": "*" - } - }, - "node_modules/npm-packlist/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/npm-pick-manifest": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.0.0.tgz", - "integrity": "sha512-PdJpXMvjqt4nftNEDpCgjBUF8yI3Q3MyuAmVB9nemnnCg32F4BPL/JFBfdj8DubgHCYUFQhtLWmBPvdsFtjWMg==", - "dev": true, - "dependencies": { - "npm-install-checks": "^4.0.0", - "npm-package-arg": "^8.0.0", - "semver": "^7.0.0" - } - }, - "node_modules/npm-pick-manifest/node_modules/semver": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", - "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm-registry-fetch": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-8.0.0.tgz", - "integrity": "sha512-975WwLvZjX97y9UWWQ8nAyr7bw02s9xKPHqvEm5T900LQsB1HXb8Gb9ebYtCBLSX+K8gSOrO5KS/9yV/naLZmQ==", - "dev": true, - "dependencies": { - "@npmcli/ci-detect": "^1.0.0", - "lru-cache": "^5.1.1", - "make-fetch-happen": "^8.0.2", - "minipass": "^3.0.0", - "minipass-fetch": "^1.1.2", - "minipass-json-stream": "^1.0.1", - "minizlib": "^2.0.0", - "npm-package-arg": "^8.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm-registry-fetch/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/npm-registry-fetch/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "dependencies": { - "path-key": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "dev": true, - "dependencies": { - "boolbase": "~1.0.0" - } - }, - "node_modules/num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", - "dev": true - }, - "node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/object-assign": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-0.3.1.tgz", - "integrity": "sha1-Bg4qKifXwNd+x3t48Rqkf9iACNI=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-component": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", - "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", - "dev": true - }, - "node_modules/object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "dependencies": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", - "dev": true - }, - "node_modules/object-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.2.tgz", - "integrity": "sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "dependencies": { - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.getownpropertydescriptors": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", - "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.values": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", - "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", - "has": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true - }, - "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/open": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/open/-/open-7.0.3.tgz", - "integrity": "sha512-sP2ru2v0P290WFfv49Ap8MF6PkzGNnGlAwHweB4WR4mr5d2d0woiCluUeJ218w7/+PmoBy9JmYgD5A4mLcWOFA==", - "dev": true, - "dependencies": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/opn": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", - "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", - "dev": true, - "dependencies": { - "is-wsl": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/opn/node_modules/is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, - "dependencies": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" - } - }, - "node_modules/optimist/node_modules/minimist": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", - "dev": true - }, - "node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "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" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ora": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/ora/-/ora-4.0.3.tgz", - "integrity": "sha512-fnDebVFyz309A73cqCipVL1fBZewq4vwgSHfxh43vVy31mbyoQ8sCH3Oeaog/owYOs/lLlGVPCISQonTneg6Pg==", - "dev": true, - "dependencies": { - "chalk": "^3.0.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.2.0", - "is-interactive": "^1.0.0", - "log-symbols": "^3.0.0", - "mute-stream": "0.0.8", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "dependencies": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/ora/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/original": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", - "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", - "dev": true, - "dependencies": { - "url-parse": "^1.4.3" - } - }, - "node_modules/os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", - "dev": true - }, - "node_modules/os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "dependencies": { - "lcid": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-retry": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", - "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", - "dev": true, - "dependencies": { - "retry": "^0.12.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/pac-proxy-agent": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-0.2.0.tgz", - "integrity": "sha1-rZApCdkvT+fMLl9Z9b9QYbz6cbI=", - "dependencies": { - "agent-base": "~1.0.1", - "debug": "2", - "extend": "~1.2.1", - "get-uri": "~0.1.0", - "pac-resolver": "~1.2.1", - "proxy-agent": "1", - "stream-to-array": "~1.0.0" - } - }, - "node_modules/pac-proxy-agent/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/pac-proxy-agent/node_modules/extend": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-1.2.1.tgz", - "integrity": "sha1-oPX9bPyDpf5J72mNYOyKYk3UV2w=" - }, - "node_modules/pac-proxy-agent/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/pac-resolver": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-1.2.6.tgz", - "integrity": "sha1-7QOvDFtZM1Bb3T8H91F1Rm1efPs=", - "dependencies": { - "co": "~3.0.6", - "degenerator": "~1.0.0", - "netmask": "~1.0.4", - "regenerator": "~0.8.13", - "thunkify": "~2.1.1" - } - }, - "node_modules/pac-resolver/node_modules/co": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/co/-/co-3.0.6.tgz", - "integrity": "sha1-FEXyJsXrlWE45oyawwFn6n0ua9o=" - }, - "node_modules/pacote": { - "version": "11.1.4", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-11.1.4.tgz", - "integrity": "sha512-eUGJvSSpWFZKn3z8gig/HgnBmUl6gIWByIIaHzSyEr3tOWX0w8tFEADXtpu8HGv5E0ShCeTP6enRq8iHKCHSvw==", - "dev": true, - "dependencies": { - "@npmcli/git": "^2.0.1", - "@npmcli/installed-package-contents": "^1.0.5", - "@npmcli/promise-spawn": "^1.1.0", - "cacache": "^15.0.0", - "chownr": "^1.1.4", - "fs-minipass": "^2.1.0", - "infer-owner": "^1.0.4", - "lru-cache": "^5.1.1", - "minipass": "^3.0.1", - "minipass-fetch": "^1.2.1", - "mkdirp": "^1.0.3", - "npm-package-arg": "^8.0.1", - "npm-packlist": "^2.1.0", - "npm-pick-manifest": "^6.0.0", - "npm-registry-fetch": "^8.0.0", - "promise-inflight": "^1.0.1", - "promise-retry": "^1.1.1", - "read-package-json-fast": "^1.1.3", - "rimraf": "^2.7.1", - "semver": "^7.1.3", - "ssri": "^8.0.0", - "tar": "^6.0.1", - "which": "^2.0.2" - }, - "bin": { - "pacote": "lib/bin.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/pacote/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/pacote/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/pacote/node_modules/semver": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", - "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/pacote/node_modules/tar": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.0.1.tgz", - "integrity": "sha512-bKhKrrz2FJJj5s7wynxy/fyxpE0CmCjmOQ1KV4KkgXFWOgoIT/NbTMnB1n+LFNrNk0SSBVGGxcK5AGsyC+pW5Q==", - "dev": true, - "dependencies": { - "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" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/pacote/node_modules/tar/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/pacote/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/pacote/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - }, - "node_modules/parallel-transform": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", - "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", - "dev": true, - "dependencies": { - "cyclist": "^1.0.1", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" - } - }, - "node_modules/parse-asn1": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", - "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", - "dev": true, - "dependencies": { - "asn1.js": "^4.0.0", - "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" - } - }, - "node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", - "optional": true - }, - "node_modules/parseqs": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", - "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", - "dev": true, - "dependencies": { - "better-assert": "~1.0.0" - } - }, - "node_modules/parseuri": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", - "dev": true, - "dependencies": { - "better-assert": "~1.0.0" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", - "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", - "dev": true - }, - "node_modules/path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true - }, - "node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", - "dev": true - }, - "node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/path-type/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", - "dependencies": { - "through": "~2.3" - } - }, - "node_modules/pbkdf2": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", - "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", - "dev": true, - "dependencies": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "dependencies": { - "pinkie": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-dir/node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", - "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", - "dev": true, - "dependencies": { - "find-up": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/portfinder": { - "version": "1.0.25", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.25.tgz", - "integrity": "sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg==", - "dev": true, - "dependencies": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.1" - }, - "engines": { - "node": ">= 0.12.0" - } - }, - "node_modules/portfinder/node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/portfinder/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-2.2.6.tgz", - "integrity": "sha1-wENE4kSeRYa5Vfvkp093CA2EVx8=", - "dependencies": { - "js-base64": "~2.1.5", - "source-map": "~0.1.40" - } - }, - "node_modules/postcss-calc": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.2.tgz", - "integrity": "sha512-rofZFHUg6ZIrvRwPeFktv06GdbDYLcGqh9EwiMutZg+a0oePCCw1zHOEiji6LCpyRcjTREtPASuUqeAvYlEVvQ==", - "dev": true, - "dependencies": { - "postcss": "^7.0.27", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.0.2" - } - }, - "node_modules/postcss-calc/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-calc/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-calc/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-colormin": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", - "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", - "dev": true, - "dependencies": { - "browserslist": "^4.0.0", - "color": "^3.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-colormin/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-colormin/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "node_modules/postcss-colormin/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-colormin/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-convert-values": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", - "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", - "dev": true, - "dependencies": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-convert-values/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-convert-values/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "node_modules/postcss-convert-values/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-convert-values/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-discard-comments": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", - "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", - "dev": true, - "dependencies": { - "postcss": "^7.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-discard-comments/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-discard-comments/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-discard-comments/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-discard-duplicates": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", - "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", - "dev": true, - "dependencies": { - "postcss": "^7.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-discard-duplicates/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-discard-duplicates/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-discard-duplicates/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-discard-empty": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", - "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", - "dev": true, - "dependencies": { - "postcss": "^7.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-discard-empty/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-discard-empty/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-discard-empty/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-discard-overridden": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", - "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", - "dev": true, - "dependencies": { - "postcss": "^7.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-discard-overridden/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-discard-overridden/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-discard-overridden/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-import": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-12.0.1.tgz", - "integrity": "sha512-3Gti33dmCjyKBgimqGxL3vcV8w9+bsHwO5UrBawp796+jdardbcFl4RP5w/76BwNL7aGzpKstIfF9I+kdE8pTw==", - "dev": true, - "dependencies": { - "postcss": "^7.0.1", - "postcss-value-parser": "^3.2.3", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-import/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-import/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "node_modules/postcss-import/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-import/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-load-config": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz", - "integrity": "sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q==", - "dev": true, - "dependencies": { - "cosmiconfig": "^5.0.0", - "import-cwd": "^2.0.0" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/postcss-loader": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", - "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", - "dev": true, - "dependencies": { - "loader-utils": "^1.1.0", - "postcss": "^7.0.0", - "postcss-load-config": "^2.0.0", - "schema-utils": "^1.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/postcss-loader/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/postcss-loader/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/postcss-loader/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-loader/node_modules/schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "dependencies": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/postcss-loader/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-loader/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-merge-longhand": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", - "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", - "dev": true, - "dependencies": { - "css-color-names": "0.0.4", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "stylehacks": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-merge-longhand/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-merge-longhand/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "node_modules/postcss-merge-longhand/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-merge-longhand/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-merge-rules": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", - "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", - "dev": true, - "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" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-merge-rules/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-merge-rules/node_modules/postcss-selector-parser": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", - "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", - "dev": true, - "dependencies": { - "dot-prop": "^5.2.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/postcss-merge-rules/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-merge-rules/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-minify-font-values": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", - "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", - "dev": true, - "dependencies": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-minify-font-values/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-minify-font-values/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "node_modules/postcss-minify-font-values/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-minify-font-values/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-minify-gradients": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", - "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", - "dev": true, - "dependencies": { - "cssnano-util-get-arguments": "^4.0.0", - "is-color-stop": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-minify-gradients/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-minify-gradients/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "node_modules/postcss-minify-gradients/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-minify-gradients/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-minify-params": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", - "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", - "dev": true, - "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" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-minify-params/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-minify-params/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "node_modules/postcss-minify-params/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-minify-params/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-minify-selectors": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", - "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", - "dev": true, - "dependencies": { - "alphanum-sort": "^1.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-minify-selectors/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-minify-selectors/node_modules/postcss-selector-parser": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", - "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", - "dev": true, - "dependencies": { - "dot-prop": "^5.2.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/postcss-minify-selectors/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-minify-selectors/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-normalize-charset": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", - "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", - "dev": true, - "dependencies": { - "postcss": "^7.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-normalize-charset/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-normalize-charset/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-normalize-charset/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-normalize-display-values": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", - "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", - "dev": true, - "dependencies": { - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-normalize-display-values/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-normalize-display-values/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "node_modules/postcss-normalize-display-values/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-normalize-display-values/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-normalize-positions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", - "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", - "dev": true, - "dependencies": { - "cssnano-util-get-arguments": "^4.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-normalize-positions/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-normalize-positions/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "node_modules/postcss-normalize-positions/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-normalize-positions/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-normalize-repeat-style": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", - "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", - "dev": true, - "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" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-normalize-repeat-style/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-normalize-repeat-style/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "node_modules/postcss-normalize-repeat-style/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-normalize-repeat-style/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-normalize-string": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", - "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", - "dev": true, - "dependencies": { - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-normalize-string/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-normalize-string/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "node_modules/postcss-normalize-string/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-normalize-string/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-normalize-timing-functions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", - "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", - "dev": true, - "dependencies": { - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-normalize-timing-functions/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-normalize-timing-functions/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "node_modules/postcss-normalize-timing-functions/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-normalize-timing-functions/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-normalize-unicode": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", - "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", - "dev": true, - "dependencies": { - "browserslist": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-normalize-unicode/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-normalize-unicode/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "node_modules/postcss-normalize-unicode/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-normalize-unicode/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-normalize-url": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", - "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", - "dev": true, - "dependencies": { - "is-absolute-url": "^2.0.0", - "normalize-url": "^3.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-normalize-url/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-normalize-url/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "node_modules/postcss-normalize-url/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-normalize-url/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-normalize-whitespace": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", - "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", - "dev": true, - "dependencies": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-normalize-whitespace/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-normalize-whitespace/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "node_modules/postcss-normalize-whitespace/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-normalize-whitespace/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-ordered-values": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", - "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", - "dev": true, - "dependencies": { - "cssnano-util-get-arguments": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-ordered-values/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-ordered-values/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "node_modules/postcss-ordered-values/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-ordered-values/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-reduce-initial": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", - "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", - "dev": true, - "dependencies": { - "browserslist": "^4.0.0", - "caniuse-api": "^3.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-reduce-initial/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-reduce-initial/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-reduce-initial/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-reduce-transforms": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", - "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", - "dev": true, - "dependencies": { - "cssnano-util-get-match": "^4.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-reduce-transforms/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-reduce-transforms/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "node_modules/postcss-reduce-transforms/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-reduce-transforms/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz", - "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==", - "dev": true, - "dependencies": { - "cssesc": "^3.0.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-svgo": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz", - "integrity": "sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==", - "dev": true, - "dependencies": { - "is-svg": "^3.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "svgo": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-svgo/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-svgo/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "node_modules/postcss-svgo/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-svgo/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-unique-selectors": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", - "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", - "dev": true, - "dependencies": { - "alphanum-sort": "^1.0.0", - "postcss": "^7.0.0", - "uniqs": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/postcss-unique-selectors/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-unique-selectors/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-unique-selectors/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz", - "integrity": "sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg==", - "dev": true - }, - "node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true, - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "node_modules/promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "dev": true, - "optional": true, - "dependencies": { - "asap": "~2.0.3" - } - }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", - "dev": true - }, - "node_modules/promise-retry": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz", - "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=", - "dev": true, - "dependencies": { - "err-code": "^1.0.0", - "retry": "^0.10.0" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/promise-retry/node_modules/retry": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", - "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/protractor": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.4.3.tgz", - "integrity": "sha512-7pMAolv8Ah1yJIqaorDTzACtn3gk7BamVKPTeO5lqIGOrfosjPgXFx/z1dqSI+m5EeZc2GMJHPr5DYlodujDNA==", - "dev": true, - "dependencies": { - "@types/q": "^0.0.32", - "@types/selenium-webdriver": "^3.0.0", - "blocking-proxy": "^1.0.0", - "browserstack": "^1.5.1", - "chalk": "^1.1.3", - "glob": "^7.0.3", - "jasmine": "2.8.0", - "jasminewd2": "^2.1.0", - "optimist": "~0.6.0", - "q": "1.4.1", - "saucelabs": "^1.5.0", - "selenium-webdriver": "3.6.0", - "source-map-support": "~0.4.0", - "webdriver-js-extender": "2.1.0", - "webdriver-manager": "^12.0.6" - }, - "bin": { - "protractor": "bin/protractor", - "webdriver-manager": "bin/webdriver-manager" - }, - "engines": { - "node": ">=6.9.x" - } - }, - "node_modules/protractor/node_modules/@types/q": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", - "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=", - "dev": true - }, - "node_modules/protractor/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/protractor/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "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" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/protractor/node_modules/del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", - "dev": true, - "dependencies": { - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/protractor/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "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" - }, - "engines": { - "node": "*" - } - }, - "node_modules/protractor/node_modules/globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", - "dev": true, - "dependencies": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/protractor/node_modules/is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/protractor/node_modules/is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", - "dev": true, - "dependencies": { - "is-path-inside": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/protractor/node_modules/is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "dependencies": { - "path-is-inside": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/protractor/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/protractor/node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/protractor/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/protractor/node_modules/q": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", - "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", - "dev": true, - "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" - } - }, - "node_modules/protractor/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/protractor/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/protractor/node_modules/source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "dev": true, - "dependencies": { - "source-map": "^0.5.6" - } - }, - "node_modules/protractor/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/protractor/node_modules/webdriver-manager": { - "version": "12.1.7", - "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.7.tgz", - "integrity": "sha512-XINj6b8CYuUYC93SG3xPkxlyUc3IJbD6Vvo75CVGuG9uzsefDzWQrhz0Lq8vbPxtb4d63CZdYophF8k8Or/YiA==", - "dev": true, - "dependencies": { - "adm-zip": "^0.4.9", - "chalk": "^1.1.1", - "del": "^2.2.0", - "glob": "^7.0.3", - "ini": "^1.3.4", - "minimist": "^1.2.0", - "q": "^1.4.1", - "request": "^2.87.0", - "rimraf": "^2.5.2", - "semver": "^5.3.0", - "xml2js": "^0.4.17" - }, - "bin": { - "webdriver-manager": "bin/webdriver-manager" - }, - "engines": { - "node": ">=6.9.x" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", - "dev": true, - "dependencies": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-agent": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-1.1.1.tgz", - "integrity": "sha1-/LHu9eWJZcmV+TjwKdcp/IGFi5U=", - "dependencies": { - "http-proxy-agent": "0", - "https-proxy-agent": "0", - "lru-cache": "~2.5.0", - "pac-proxy-agent": "0", - "socks-proxy-agent": "1" - } - }, - "node_modules/proxy-agent/node_modules/lru-cache": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.2.tgz", - "integrity": "sha1-H92tk4quEmPOE4aAvhs/WRwKtBw=" - }, - "node_modules/prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", - "dev": true - }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "node_modules/psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true - }, - "node_modules/public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "dev": true, - "dependencies": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "dev": true, - "dependencies": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - } - }, - "node_modules/pumpify/node_modules/pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", - "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" - } - }, - "node_modules/qjobs": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", - "dev": true, - "engines": { - "node": ">=0.9" - } - }, - "node_modules/qs": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.5.tgz", - "integrity": "sha1-KUsmjksNQlD23eGbO4s0k13/FO8=", - "engines": { - "node": "*" - } - }, - "node_modules/query-string": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", - "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", - "dev": true, - "dependencies": { - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/query-string/node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true, - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true, - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/querystringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", - "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", - "dev": true - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "dependencies": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.3.4.tgz", - "integrity": "sha1-zMfd/Ea3KGHN1btDPIQLcLbyf1Q=", - "dependencies": { - "bytes": "1.0.0", - "iconv-lite": "0.4.8" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.4.8", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.8.tgz", - "integrity": "sha1-xgGadZXyzvynAuq2lKAQvNkpjSA=", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/raw-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.0.tgz", - "integrity": "sha512-iINUOYvl1cGEmfoaLjnZXt4bKfT2LJnZZib5N/LLyAphC+Dd11vNP9CNVb38j+SAJpFI1uo8j9frmih53ASy7Q==", - "dev": true, - "dependencies": { - "loader-utils": "^1.2.3", - "schema-utils": "^2.5.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/raw-loader/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/raw-loader/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", - "dev": true, - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/read-cache/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/read-package-json": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.1.tgz", - "integrity": "sha512-dAiqGtVc/q5doFz6096CcnXhpYk0ZN8dEKVkGLU0CsASt8SrgF6SF7OTKAYubfvFhWaqofl+Y8HK19GR8jwW+A==", - "dev": true, - "dependencies": { - "glob": "^7.1.1", - "graceful-fs": "^4.1.2", - "json-parse-better-errors": "^1.0.1", - "normalize-package-data": "^2.0.0", - "npm-normalize-package-bin": "^1.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.2" - } - }, - "node_modules/read-package-json-fast": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-1.1.3.tgz", - "integrity": "sha512-MmFqiyfCXV2Dmm4jH24DEGhxdkUDFivJQj4oPZQPOKywxR7HWBE6WnMWDAapfFHi3wm1b+mhR+XHlUH0CL8axg==", - "dev": true, - "dependencies": { - "json-parse-even-better-errors": "^2.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "node_modules/read-package-json/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "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" - }, - "engines": { - "node": "*" - } - }, - "node_modules/read-package-json/node_modules/graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true, - "optional": true - }, - "node_modules/read-package-json/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/read-package-tree": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz", - "integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==", - "dev": true, - "dependencies": { - "read-package-json": "^2.0.0", - "readdir-scoped-modules": "^1.0.0", - "util-promisify": "^2.1.0" - } - }, - "node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/readable-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "node_modules/readable-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/readdir-scoped-modules": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", - "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", - "dev": true, - "dependencies": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" - } - }, - "node_modules/readdir-scoped-modules/node_modules/graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, - "node_modules/readdirp": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz", - "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==", - "dev": true, - "dependencies": { - "picomatch": "^2.0.7" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/recast": { - "version": "0.10.33", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.10.33.tgz", - "integrity": "sha1-lCgI96oBbx+nFCxGHX5XBKqo1pc=", - "dependencies": { - "ast-types": "0.8.12", - "esprima-fb": "~15001.1001.0-dev-harmony-fb", - "private": "~0.1.5", - "source-map": "~0.5.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/recast/node_modules/ast-types": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.8.12.tgz", - "integrity": "sha1-oNkOQ1G7iHcWyD/WN+v4GK9K38w=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/recast/node_modules/esprima-fb": { - "version": "15001.1001.0-dev-harmony-fb", - "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz", - "integrity": "sha1-Q761fsJujPI3092LM+QlM1d/Jlk=", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/recast/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/reduce-component": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/reduce-component/-/reduce-component-1.0.1.tgz", - "integrity": "sha1-4Mk1QsV0UhvqE98PlIjtgqt3xdo=" - }, - "node_modules/reflect-metadata": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", - "dev": true - }, - "node_modules/regenerate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", - "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", - "dev": true - }, - "node_modules/regenerate-unicode-properties": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", - "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", - "dev": true, - "dependencies": { - "regenerate": "^1.4.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator": { - "version": "0.8.46", - "resolved": "https://registry.npmjs.org/regenerator/-/regenerator-0.8.46.tgz", - "integrity": "sha1-FUwydoY2HtUsrWmyVF78U6PQdpY=", - "dependencies": { - "commoner": "~0.10.3", - "defs": "~1.1.0", - "esprima-fb": "~15001.1001.0-dev-harmony-fb", - "private": "~0.1.5", - "recast": "0.10.33", - "regenerator-runtime": "~0.9.5", - "through": "~2.3.8" - }, - "bin": { - "regenerator": "bin/regenerator" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.9.6", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.9.6.tgz", - "integrity": "sha1-0z65XQ0gAaS+OWWXB8UbDLcc4Ck=" - }, - "node_modules/regenerator-transform": { - "version": "0.14.4", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.4.tgz", - "integrity": "sha512-EaJaKPBI9GvKpvUz2mz4fhx7WPgvwRLY9v3hlNHWmAuJHI13T4nwKnNvm5RWJzEdnI5g5UwtOww+S8IdoUC2bw==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.8.4", - "private": "^0.1.8" - } - }, - "node_modules/regenerator/node_modules/esprima-fb": { - "version": "15001.1001.0-dev-harmony-fb", - "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz", - "integrity": "sha1-Q761fsJujPI3092LM+QlM1d/Jlk=", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "dependencies": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/regexp.prototype.flags": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", - "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/regexpu-core": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz", - "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==", - "dev": true, - "dependencies": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.2.0", - "regjsgen": "^0.5.1", - "regjsparser": "^0.6.4", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.2.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regjsgen": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz", - "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==", - "dev": true - }, - "node_modules/regjsparser": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", - "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", - "dev": true, - "dependencies": { - "jsesc": "~0.5.0" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - } - }, - "node_modules/remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "node_modules/repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dev": true, - "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" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/request/node_modules/qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/request/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true, - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "node_modules/requires": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/requires/-/requires-1.0.2.tgz", - "integrity": "sha1-djBOghNFYi/j+sCwcRoeTygo8Po=" - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true - }, - "node_modules/resolve": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", - "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", - "dev": true, - "dependencies": { - "path-parse": "^1.0.6" - } - }, - "node_modules/resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", - "dev": true, - "dependencies": { - "resolve-from": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/rfdc": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz", - "integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==", - "dev": true - }, - "node_modules/rgb-regex": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", - "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=", - "dev": true - }, - "node_modules/rgba-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", - "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", - "dev": true - }, - "node_modules/right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "dependencies": { - "align-text": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "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" - }, - "engines": { - "node": "*" - } - }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "node_modules/rollup": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.1.0.tgz", - "integrity": "sha512-gfE1455AEazVVTJoeQtcOq/U6GSxwoj4XPSWVsuWmgIxj7sBQNLDOSA82PbdMe+cP8ql8fR1jogPFe8Wg8g4SQ==", - "dev": true, - "dependencies": { - "fsevents": "~2.1.2" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=10.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.1.2" - } - }, - "node_modules/run-async": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz", - "integrity": "sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==", - "dev": true, - "dependencies": { - "is-promise": "^2.1.0" - }, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", - "dev": true, - "dependencies": { - "aproba": "^1.1.1" - } - }, - "node_modules/rxjs": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", - "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "dependencies": { - "ret": "~0.1.10" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/sane": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/sane/-/sane-0.8.1.tgz", - "integrity": "sha1-JDpLIECBvlWTwKIrC7sRVzUS/Mc=", - "dependencies": { - "minimatch": "~0.2.14", - "walker": "~1.0.5", - "watch": "~0.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/sass": { - "version": "1.26.3", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.26.3.tgz", - "integrity": "sha512-5NMHI1+YFYw4sN3yfKjpLuV9B5l7MqQ6FlkTcC4FT+oHbBRUZoSjHrrt/mE0nFXJyY2kQtU9ou9HxvFVjLFuuw==", - "dev": true, - "dependencies": { - "chokidar": ">=2.0.0 <4.0.0" - }, - "bin": { - "sass": "sass.js" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/sass-loader": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-8.0.2.tgz", - "integrity": "sha512-7o4dbSK8/Ol2KflEmSco4jTjQoV988bM82P9CZdmo9hR3RLnvNc0ufMNdMrB0caq38JQ/FgF4/7RcbcfKzxoFQ==", - "dev": true, - "dependencies": { - "clone-deep": "^4.0.1", - "loader-utils": "^1.2.3", - "neo-async": "^2.6.1", - "schema-utils": "^2.6.1", - "semver": "^6.3.0" - }, - "engines": { - "node": ">= 8.9.0" - } - }, - "node_modules/sass-loader/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/sass-loader/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/sass-loader/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/saucelabs": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz", - "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==", - "dev": true, - "dependencies": { - "https-proxy-agent": "^2.2.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/saucelabs/node_modules/agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "dependencies": { - "es6-promisify": "^5.0.0" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/saucelabs/node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/saucelabs/node_modules/https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "dev": true, - "dependencies": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - }, - "engines": { - "node": ">= 4.5.0" - } - }, - "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, - "node_modules/schema-utils": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.5.tgz", - "integrity": "sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ==", - "dev": true, - "dependencies": { - "ajv": "^6.12.0", - "ajv-keywords": "^3.4.1" - }, - "engines": { - "node": ">= 8.9.0" - } - }, - "node_modules/select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", - "dev": true - }, - "node_modules/selenium-webdriver": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", - "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", - "dev": true, - "dependencies": { - "jszip": "^3.1.3", - "rimraf": "^2.5.4", - "tmp": "0.0.30", - "xml2js": "^0.4.17" - }, - "engines": { - "node": ">= 6.9.0" - } - }, - "node_modules/selenium-webdriver/node_modules/tmp": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", - "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", - "dev": true, - "dependencies": { - "os-tmpdir": "~1.0.1" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/selfsigned": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz", - "integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==", - "dev": true, - "dependencies": { - "node-forge": "0.9.0" - } - }, - "node_modules/semver": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-2.3.2.tgz", - "integrity": "sha1-uYSPJdbPNjMwc+ye+IVtQvEjPlI=", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/semver-dsl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz", - "integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=", - "dev": true, - "dependencies": { - "semver": "^5.3.0" - } - }, - "node_modules/semver-dsl/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/semver-intersect": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/semver-intersect/-/semver-intersect-1.4.0.tgz", - "integrity": "sha512-d8fvGg5ycKAq0+I6nfWeCx6ffaWJCsBYU0H2Rq56+/zFePYfT8mXkB3tWBSjR5BerkHNZ5eTPIk1/LBYas35xQ==", - "dev": true, - "dependencies": { - "semver": "^5.0.0" - } - }, - "node_modules/semver-intersect/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "dev": true, - "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" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/send/node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "node_modules/serialize-javascript": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", - "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", - "dev": true - }, - "node_modules/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", - "dev": true, - "dependencies": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/serve-index/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/serve-index/node_modules/http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "dev": true, - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "node_modules/serve-index/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/serve-index/node_modules/setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - }, - "node_modules/serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "dev": true, - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "node_modules/set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/set-value/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true - }, - "node_modules/setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", - "dev": true - }, - "node_modules/sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - }, - "bin": { - "sha.js": "bin.js" - } - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shallow-clone/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sigmund": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", - "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" - }, - "node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "node_modules/simple-fmt": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/simple-fmt/-/simple-fmt-0.1.0.tgz", - "integrity": "sha1-GRv1ZqWeZTBILLJatTtKjchcOms=" - }, - "node_modules/simple-is": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/simple-is/-/simple-is-0.2.0.tgz", - "integrity": "sha1-Krt1qt453rXMgVzhDmGRFkhQuvA=" - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "dev": true, - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "dev": true - }, - "node_modules/slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/smart-buffer": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-1.1.15.tgz", - "integrity": "sha1-fxFLW2X6s+KjWqd1uxLw0cZJvxY=", - "engines": { - "node": ">= 0.10.15", - "npm": ">= 1.3.5" - } - }, - "node_modules/snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "dependencies": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "dependencies": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "dependencies": { - "kind-of": "^3.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/snapdragon/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/snapdragon/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/socket.io": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz", - "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==", - "dev": true, - "dependencies": { - "debug": "~3.1.0", - "engine.io": "~3.2.0", - "has-binary2": "~1.0.2", - "socket.io-adapter": "~1.1.0", - "socket.io-client": "2.1.1", - "socket.io-parser": "~3.2.0" - } - }, - "node_modules/socket.io-adapter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz", - "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==", - "dev": true - }, - "node_modules/socket.io-client": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz", - "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==", - "dev": true, - "dependencies": { - "backo2": "1.0.2", - "base64-arraybuffer": "0.1.5", - "component-bind": "1.0.0", - "component-emitter": "1.2.1", - "debug": "~3.1.0", - "engine.io-client": "~3.2.0", - "has-binary2": "~1.0.2", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "object-component": "0.0.3", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "socket.io-parser": "~3.2.0", - "to-array": "0.1.4" - } - }, - "node_modules/socket.io-client/node_modules/component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "node_modules/socket.io-client/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/socket.io-client/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/socket.io-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", - "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==", - "dev": true, - "dependencies": { - "component-emitter": "1.2.1", - "debug": "~3.1.0", - "isarray": "2.0.1" - } - }, - "node_modules/socket.io-parser/node_modules/component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "node_modules/socket.io-parser/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/socket.io-parser/node_modules/isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", - "dev": true - }, - "node_modules/socket.io-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/socket.io/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/socket.io/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/sockjs": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", - "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", - "dev": true, - "dependencies": { - "faye-websocket": "^0.10.0", - "uuid": "^3.0.1" - } - }, - "node_modules/sockjs-client": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz", - "integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==", - "dev": true, - "dependencies": { - "debug": "^3.2.5", - "eventsource": "^1.0.7", - "faye-websocket": "~0.11.1", - "inherits": "^2.0.3", - "json3": "^3.3.2", - "url-parse": "^1.4.3" - } - }, - "node_modules/sockjs-client/node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/sockjs-client/node_modules/faye-websocket": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", - "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", - "dev": true, - "dependencies": { - "websocket-driver": ">=0.5.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/sockjs/node_modules/faye-websocket": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", - "dev": true, - "dependencies": { - "websocket-driver": ">=0.5.1" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/sockjs/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true, - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/socks": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/socks/-/socks-1.1.10.tgz", - "integrity": "sha1-W4t/x8jzQcU+0FbpKbe/Tei6e1o=", - "dependencies": { - "ip": "^1.1.4", - "smart-buffer": "^1.0.13" - }, - "engines": { - "node": ">= 0.10.0", - "npm": ">= 1.3.5" - } - }, - "node_modules/socks-proxy-agent": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-1.0.2.tgz", - "integrity": "sha1-Z+BrRH/lY3QX/eVzPL/f7J/+EX8=", - "dependencies": { - "agent-base": "~1.0.1", - "extend": "~1.2.1", - "socks": "~1.1.5" - } - }, - "node_modules/socks-proxy-agent/node_modules/extend": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-1.2.1.tgz", - "integrity": "sha1-oPX9bPyDpf5J72mNYOyKYk3UV2w=" - }, - "node_modules/sort-keys": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", - "dev": true, - "dependencies": { - "is-plain-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", - "dev": true - }, - "node_modules/source-map": { - "version": "0.1.43", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", - "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", - "dependencies": { - "amdefine": ">=0.0.4" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/source-map-loader": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-0.2.4.tgz", - "integrity": "sha512-OU6UJUty+i2JDpTItnizPrlpOIBLmQbWMuBg9q5bVtnHACqw1tn9nNwqJLbv0/00JjnJb/Ee5g5WS5vrRv7zIQ==", - "dev": true, - "dependencies": { - "async": "^2.5.0", - "loader-utils": "^1.1.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/source-map-loader/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/source-map-loader/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "dev": true, - "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", - "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "dev": true - }, - "node_modules/spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", - "dev": true - }, - "node_modules/spdy": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.1.tgz", - "integrity": "sha512-HeZS3PBdMA+sZSu0qwpCxl3DeALD5ASx8pAX0jZdKXSpPWbQ6SYGnlg3BBmYLx5LtiZrmkAZfErCm2oECBcioA==", - "dev": true, - "dependencies": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "dev": true, - "dependencies": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - } - }, - "node_modules/spdy-transport/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/spdy-transport/node_modules/safe-buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", - "dev": true - }, - "node_modules/spdy-transport/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/speed-measure-webpack-plugin": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.3.1.tgz", - "integrity": "sha512-qVIkJvbtS9j/UeZumbdfz0vg+QfG/zxonAjzefZrqzkr7xOncLVXkeGbTpzd1gjCBM4PmVNkWlkeTVhgskAGSQ==", - "dev": true, - "dependencies": { - "chalk": "^2.0.1" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", - "dependencies": { - "through": "2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "dependencies": { - "extend-shallow": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "node_modules/sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "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" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ssri": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.0.tgz", - "integrity": "sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==", - "dev": true, - "dependencies": { - "minipass": "^3.1.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" - }, - "node_modules/static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "dependencies": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/stream-browserify": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", - "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", - "dev": true, - "dependencies": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" - } - }, - "node_modules/stream-combiner": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", - "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", - "dependencies": { - "duplexer": "~0.1.1" - } - }, - "node_modules/stream-each": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", - "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" - } - }, - "node_modules/stream-http": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", - "dev": true, - "dependencies": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" - } - }, - "node_modules/stream-shift": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", - "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", - "dev": true - }, - "node_modules/stream-to-array": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-to-array/-/stream-to-array-1.0.0.tgz", - "integrity": "sha1-lBZrsp8+ok8ILS+M0+uyzA1uyiw=", - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/streamroller": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-1.0.6.tgz", - "integrity": "sha512-3QC47Mhv3/aZNFpDDVO44qQb9gwB9QggMEE0sQmkTAwBVYdBRWISdsywlkfm5II1Q5y/pmrHflti/IgmIzdDBg==", - "dev": true, - "dependencies": { - "async": "^2.6.2", - "date-format": "^2.0.0", - "debug": "^3.2.6", - "fs-extra": "^7.0.1", - "lodash": "^4.17.14" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/streamroller/node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/streamroller/node_modules/fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/streamroller/node_modules/graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, - "node_modules/strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - }, - "node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.0.tgz", - "integrity": "sha512-EEJnGqa/xNfIg05SxiPSqRS7S9qwDhYts1TSLR1BQfYUfPe1stofgGKvwERK9+9yf+PpfBMlpBaCHucXGPQfUA==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "node_modules/string.prototype.trimleft": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", - "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimstart": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.trimright": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", - "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimend": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.0.tgz", - "integrity": "sha512-iCP8g01NFYiiBOnwG1Xc3WZLyoo+RuBymwIlWncShXDDJYWN6DbnM3odslBJdgCdRlq94B5s63NWAZlcn2CS4w==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "node_modules/stringmap": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stringmap/-/stringmap-0.2.2.tgz", - "integrity": "sha1-VWwTeyWPlCuHdvWy71gqoGnX0bE=" - }, - "node_modules/stringset": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/stringset/-/stringset-0.2.1.tgz", - "integrity": "sha1-7yWcTjSTRDd/zRyRPdLoSMnAQrU=" - }, - "node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/style-loader": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.1.3.tgz", - "integrity": "sha512-rlkH7X/22yuwFYK357fMN/BxYOorfnfq0eD7+vqlemSK4wEcejFF1dg4zxP0euBW8NrYx2WZzZ8PPFevr7D+Kw==", - "dev": true, - "dependencies": { - "loader-utils": "^1.2.3", - "schema-utils": "^2.6.4" - }, - "engines": { - "node": ">= 8.9.0" - } - }, - "node_modules/style-loader/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/style-loader/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/stylehacks": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", - "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", - "dev": true, - "dependencies": { - "browserslist": "^4.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/stylehacks/node_modules/postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/stylehacks/node_modules/postcss-selector-parser": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", - "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", - "dev": true, - "dependencies": { - "dot-prop": "^5.2.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/stylehacks/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stylehacks/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/stylus": { - "version": "0.54.7", - "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.7.tgz", - "integrity": "sha512-Yw3WMTzVwevT6ZTrLCYNHAFmanMxdylelL3hkWNgPMeTCpMwpV3nXjpOHuBXtFv7aiO2xRuQS6OoAdgkNcSNug==", - "dev": true, - "dependencies": { - "css-parse": "~2.0.0", - "debug": "~3.1.0", - "glob": "^7.1.3", - "mkdirp": "~0.5.x", - "safer-buffer": "^2.1.2", - "sax": "~1.2.4", - "semver": "^6.0.0", - "source-map": "^0.7.3" - }, - "bin": { - "stylus": "bin/stylus" - }, - "engines": { - "node": "*" - } - }, - "node_modules/stylus-loader": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-3.0.2.tgz", - "integrity": "sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA==", - "dev": true, - "dependencies": { - "loader-utils": "^1.0.2", - "lodash.clonedeep": "^4.5.0", - "when": "~3.6.x" - } - }, - "node_modules/stylus-loader/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/stylus-loader/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/stylus/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/stylus/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "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" - }, - "engines": { - "node": "*" - } - }, - "node_modules/stylus/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/stylus/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/stylus/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/stylus/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/stylus/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/superagent": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-0.17.0.tgz", - "integrity": "sha1-qtzVD75ak+cZkRGNeb8HFNYlu6g=", - "dependencies": { - "cookiejar": "1.3.0", - "debug": "~0.7.2", - "emitter-component": "1.0.0", - "extend": "~1.2.1", - "formidable": "1.0.14", - "methods": "0.0.1", - "mime": "1.2.5", - "qs": "0.6.5", - "reduce-component": "1.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/superagent/node_modules/debug": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz", - "integrity": "sha1-BuHqgILCyxTjmAbiLi9vdX+Srzk=", - "engines": { - "node": "*" - } - }, - "node_modules/superagent/node_modules/extend": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-1.2.1.tgz", - "integrity": "sha1-oPX9bPyDpf5J72mNYOyKYk3UV2w=" - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/svgo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", - "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", - "dev": true, - "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" - }, - "bin": { - "svgo": "bin/svgo" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/svgo/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/syntax-error": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", - "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", - "dependencies": { - "acorn-node": "^1.2.0" - } - }, - "node_modules/tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/tar": { - "version": "0.1.20", - "resolved": "https://registry.npmjs.org/tar/-/tar-0.1.20.tgz", - "integrity": "sha1-QpQLrltfIsdEg2mRJvnz8nRJyxM=", - "dependencies": { - "block-stream": "*", - "fstream": "~0.1.28", - "inherits": "2" - } - }, - "node_modules/tempfile": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-0.1.3.tgz", - "integrity": "sha1-fWtxAEcznTn4RzJ6BW2t8YMQMBA=", - "dependencies": { - "uuid": "~1.4.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/terser": { - "version": "4.6.7", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.7.tgz", - "integrity": "sha512-fmr7M1f7DBly5cX2+rFDvmGBAaaZyPrHYK4mMdHEDAdNTqXSZgSOfqsfGq2HqPGT/1V0foZZuCZFx8CHKgAk3g==", - "dev": true, - "dependencies": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.5.tgz", - "integrity": "sha512-WlWksUoq+E4+JlJ+h+U+QUzXpcsMSSNXkDy9lBVkSqDn1w23Gg29L/ary9GeJVYCGiNJJX7LnVc4bwL1N3/g1w==", - "dev": true, - "dependencies": { - "cacache": "^13.0.1", - "find-cache-dir": "^3.2.0", - "jest-worker": "^25.1.0", - "p-limit": "^2.2.2", - "schema-utils": "^2.6.4", - "serialize-javascript": "^2.1.2", - "source-map": "^0.6.1", - "terser": "^4.4.3", - "webpack-sources": "^1.4.3" - }, - "engines": { - "node": ">= 8.9.0" - } - }, - "node_modules/terser-webpack-plugin/node_modules/cacache": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz", - "integrity": "sha512-5ZvAxd05HDDU+y9BVvcqYu2LLXmPnQ0hW62h32g4xBTgL/MppR4/04NHfj/ycM2y6lmTnbw6HVi+1eN0Psba6w==", - "dev": true, - "dependencies": { - "chownr": "^1.1.2", - "figgy-pudding": "^3.5.1", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.2", - "infer-owner": "^1.0.4", - "lru-cache": "^5.1.1", - "minipass": "^3.0.0", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "p-map": "^3.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^2.7.1", - "ssri": "^7.0.0", - "unique-filename": "^1.1.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/terser-webpack-plugin/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "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" - }, - "engines": { - "node": "*" - } - }, - "node_modules/terser-webpack-plugin/node_modules/graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, - "node_modules/terser-webpack-plugin/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/terser-webpack-plugin/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/terser-webpack-plugin/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/terser-webpack-plugin/node_modules/p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/terser-webpack-plugin/node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/terser-webpack-plugin/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/terser-webpack-plugin/node_modules/ssri": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.0.tgz", - "integrity": "sha512-77/WrDZUWocK0mvA5NTRQyveUf+wsrIc6vyrxpS8tVvYBcX215QbafrJR3KtkpskIzoFLqqNuuYQvxaMjXJ/0g==", - "dev": true, - "dependencies": { - "figgy-pudding": "^3.5.1", - "minipass": "^3.1.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/terser-webpack-plugin/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/terser/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/thunkify": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/thunkify/-/thunkify-2.1.2.tgz", - "integrity": "sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0=" - }, - "node_modules/thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "dev": true - }, - "node_modules/timers-browserify": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", - "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", - "dev": true, - "dependencies": { - "setimmediate": "^1.0.4" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/timsort": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", - "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", - "dev": true - }, - "node_modules/tiny-lr-fork": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/tiny-lr-fork/-/tiny-lr-fork-0.0.5.tgz", - "integrity": "sha1-Hpnh4qhGm3NquX2X7vqYxx927Qo=", - "dependencies": { - "debug": "~0.7.0", - "faye-websocket": "~0.4.3", - "noptify": "~0.0.3", - "qs": "~0.5.2" - }, - "bin": { - "tiny-lr-fork": "bin/tiny-lr" - } - }, - "node_modules/tiny-lr-fork/node_modules/debug": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz", - "integrity": "sha1-BuHqgILCyxTjmAbiLi9vdX+Srzk=", - "engines": { - "node": "*" - } - }, - "node_modules/tiny-lr-fork/node_modules/qs": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz", - "integrity": "sha1-MbGtBYVnZRxSaSFQa5qHk5EaA4Q=", - "engines": { - "node": "*" - } - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=" - }, - "node_modules/to-array": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", - "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", - "dev": true - }, - "node_modules/to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", - "dev": true - }, - "node_modules/to-descriptor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-descriptor/-/to-descriptor-1.0.1.tgz", - "integrity": "sha1-oOZ4w068fS2uRk2DcrwhR52cK80=" - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "dependencies": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true, - "bin": { - "tree-kill": "cli.js" - } - }, - "node_modules/tryor": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/tryor/-/tryor-0.1.2.tgz", - "integrity": "sha1-gUXkynyv9ArN48z5Rui4u3W0Fys=" - }, - "node_modules/ts-node": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz", - "integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==", - "dev": true, - "dependencies": { - "arg": "^4.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.6", - "yn": "^3.0.0" - }, - "bin": { - "ts-node": "dist/bin.js" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/tslib": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", - "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==" - }, - "node_modules/tslint": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.18.0.tgz", - "integrity": "sha512-Q3kXkuDEijQ37nXZZLKErssQVnwCV/+23gFEMROi8IlbaBG6tXqLPQJ5Wjcyt/yHPKBC+hD5SzuGaMora+ZS6w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^3.2.0", - "glob": "^7.1.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.8.0", - "tsutils": "^2.29.0" - }, - "bin": { - "tslint": "bin/tslint" - }, - "engines": { - "node": ">=4.8.0" - } - }, - "node_modules/tslint/node_modules/diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/tslint/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "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" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tslint/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tslint/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/tslint/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - } - }, - "node_modules/tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", - "dev": true - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, - "node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "node_modules/typescript": { - "version": "3.7.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz", - "integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/ultron": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", - "dev": true - }, - "node_modules/underscore.string": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", - "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=", - "engines": { - "node": "*" - } - }, - "node_modules/unglob": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/unglob/-/unglob-0.1.2.tgz", - "integrity": "sha1-8uwGKE5JGhx3YRp2CouOowAnLDg=", - "dependencies": { - "generator-supported": "~0.0.1", - "glob": "~3.2.8", - "minimatch": "~0.2.14" - } - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", - "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", - "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", - "dev": true, - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^1.0.4", - "unicode-property-aliases-ecmascript": "^1.0.4" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", - "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", - "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "dependencies": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", - "dev": true - }, - "node_modules/uniqs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", - "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", - "dev": true - }, - "node_modules/unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "dev": true, - "dependencies": { - "unique-slug": "^2.0.0" - } - }, - "node_modules/unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4" - } - }, - "node_modules/universal-analytics": { - "version": "0.4.20", - "resolved": "https://registry.npmjs.org/universal-analytics/-/universal-analytics-0.4.20.tgz", - "integrity": "sha512-gE91dtMvNkjO+kWsPstHRtSwHXz0l2axqptGYp5ceg4MsuurloM0PU3pdOfpb5zBXUvyjT4PwhWK2m39uczZuw==", - "dev": true, - "dependencies": { - "debug": "^3.0.0", - "request": "^2.88.0", - "uuid": "^3.0.0" - } - }, - "node_modules/universal-analytics/node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/universal-analytics/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true, - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/unquote": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", - "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=", - "dev": true - }, - "node_modules/unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "dependencies": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "dependencies": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "dependencies": { - "isarray": "1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "node_modules/upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true, - "engines": { - "node": ">=4", - "yarn": "*" - } - }, - "node_modules/uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "node_modules/url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.0" - } - }, - "node_modules/url-parse": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", - "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", - "dev": true, - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "node_modules/url/node_modules/punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - }, - "node_modules/use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/useragent": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", - "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==", - "dev": true, - "dependencies": { - "lru-cache": "4.1.x", - "tmp": "0.0.x" - } - }, - "node_modules/useragent/node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "node_modules/useragent/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, - "node_modules/util": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", - "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", - "dev": true, - "dependencies": { - "inherits": "2.0.3" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "node_modules/util-promisify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/util-promisify/-/util-promisify-2.1.0.tgz", - "integrity": "sha1-PCI2R2xNMsX/PEcAKt18E7moKlM=", - "dev": true, - "dependencies": { - "object.getownpropertydescriptors": "^2.0.3" - } - }, - "node_modules/util.promisify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", - "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.2", - "has-symbols": "^1.0.1", - "object.getownpropertydescriptors": "^2.1.0" - } - }, - "node_modules/util/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-1.4.2.tgz", - "integrity": "sha1-RTAZ9oaWam34PNxSROfJkOzDMvw=" - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/validate-npm-package-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", - "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", - "dev": true, - "dependencies": { - "builtins": "^1.0.3" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/vendors": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", - "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", - "dev": true - }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/vm-browserify": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", - "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", - "dev": true - }, - "node_modules/void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/walker": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", - "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", - "dependencies": { - "makeerror": "1.0.x" - } - }, - "node_modules/watch": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/watch/-/watch-0.10.0.tgz", - "integrity": "sha1-d3mLLaD5kQ1ZXxrOWwwiWFIfIdw=", - "engines": [ - "node >=0.1.95" - ] - }, - "node_modules/watchpack": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.1.tgz", - "integrity": "sha512-+IF9hfUFOrYOOaKyfaI7h7dquUIOgyEMoQMLA7OP5FxegKA2+XdXThAZ9TU2kucfhDH7rfMHs1oPYziVGWRnZA==", - "dev": true, - "dependencies": { - "chokidar": "^2.1.8", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" - } - }, - "node_modules/watchpack/node_modules/anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "dependencies": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "node_modules/watchpack/node_modules/anymatch/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack/node_modules/binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack/node_modules/chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "dev": true, - "dependencies": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - }, - "optionalDependencies": { - "fsevents": "^1.2.7" - } - }, - "node_modules/watchpack/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack/node_modules/fsevents": { - "version": "1.2.12", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz", - "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==", - "bundleDependencies": [ - "abbrev", - "ansi-regex", - "aproba", - "are-we-there-yet", - "balanced-match", - "brace-expansion", - "chownr", - "code-point-at", - "concat-map", - "console-control-strings", - "core-util-is", - "debug", - "deep-extend", - "delegates", - "detect-libc", - "fs-minipass", - "fs.realpath", - "gauge", - "glob", - "has-unicode", - "iconv-lite", - "ignore-walk", - "inflight", - "inherits", - "ini", - "is-fullwidth-code-point", - "isarray", - "minimatch", - "minimist", - "minipass", - "minizlib", - "mkdirp", - "ms", - "needle", - "node-pre-gyp", - "nopt", - "npm-bundled", - "npm-normalize-package-bin", - "npm-packlist", - "npmlog", - "number-is-nan", - "object-assign", - "once", - "os-homedir", - "os-tmpdir", - "osenv", - "path-is-absolute", - "process-nextick-args", - "rc", - "readable-stream", - "rimraf", - "safe-buffer", - "safer-buffer", - "sax", - "semver", - "set-blocking", - "signal-exit", - "string-width", - "string_decoder", - "strip-ansi", - "strip-json-comments", - "tar", - "util-deprecate", - "wide-align", - "wrappy", - "yallist" - ], - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1", - "node-pre-gyp": "*" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "extraneous": true, - "inBundle": true, - "license": "Apache-2.0", - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^2.6.0" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "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" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minimatch": "^3.0.4" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "*" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "minipass": "^2.9.0" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/mkdirp": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", - "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", - "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/needle": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.3.3.tgz", - "integrity": "sha512-EkY0GeSq87rWp1hoq/sH/wnTWgFVhYlnIkbJ0YJFfRgEFlz2RraCjBpFQ+vrEgEdp0ThfyHADmkChEhcb7PKyw==", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - }, - "bin": { - "needle": "bin/needle" - }, - "engines": { - "node": ">= 4.4.x" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/node-pre-gyp": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz", - "integrity": "sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA==", - "extraneous": true, - "inBundle": true, - "license": "BSD-3-Clause", - "dependencies": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4.4.2" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "abbrev": "1", - "osenv": "^0.1.4" - }, - "bin": { - "nopt": "bin/nopt.js" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/npm-bundled": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/npm-packlist": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", - "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "extraneous": true, - "inBundle": true, - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - }, - "engines": { - "node": ">=4.5" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/watchpack/node_modules/fsevents/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/watchpack/node_modules/graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, - "node_modules/watchpack/node_modules/is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "dependencies": { - "binary-extensions": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack/node_modules/readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/watchpack/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "dev": true, - "dependencies": { - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", - "dev": true, - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/webdriver-js-extender": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz", - "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==", - "dev": true, - "dependencies": { - "@types/selenium-webdriver": "^3.0.0", - "selenium-webdriver": "^3.0.1" - }, - "engines": { - "node": ">=6.9.x" - } - }, - "node_modules/webpack": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.42.0.tgz", - "integrity": "sha512-EzJRHvwQyBiYrYqhyjW9AqM90dE4+s1/XtCfn7uWg6cS72zH+2VPFAlsnW0+W0cDi0XRjNKUMoJtpSi50+Ph6w==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-module-context": "1.8.5", - "@webassemblyjs/wasm-edit": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5", - "acorn": "^6.2.1", - "ajv": "^6.10.2", - "ajv-keywords": "^3.4.1", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^4.1.0", - "eslint-scope": "^4.0.3", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^2.4.0", - "loader-utils": "^1.2.3", - "memory-fs": "^0.4.1", - "micromatch": "^3.1.10", - "mkdirp": "^0.5.1", - "neo-async": "^2.6.1", - "node-libs-browser": "^2.2.1", - "schema-utils": "^1.0.0", - "tapable": "^1.1.3", - "terser-webpack-plugin": "^1.4.3", - "watchpack": "^1.6.0", - "webpack-sources": "^1.4.1" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/webpack-dev-middleware": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz", - "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==", - "dev": true, - "dependencies": { - "memory-fs": "^0.4.1", - "mime": "^2.4.4", - "mkdirp": "^0.5.1", - "range-parser": "^1.2.1", - "webpack-log": "^2.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/webpack-dev-middleware/node_modules/memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dev": true, - "dependencies": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "node_modules/webpack-dev-middleware/node_modules/mime": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", - "dev": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/webpack-dev-middleware/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/webpack-dev-server": { - "version": "3.10.3", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.10.3.tgz", - "integrity": "sha512-e4nWev8YzEVNdOMcNzNeCN947sWJNd43E5XvsJzbAL08kGc2frm1tQ32hTJslRS+H65LCb/AaUCYU7fjHCpDeQ==", - "dev": true, - "dependencies": { - "ansi-html": "0.0.7", - "bonjour": "^3.5.0", - "chokidar": "^2.1.8", - "compression": "^1.7.4", - "connect-history-api-fallback": "^1.6.0", - "debug": "^4.1.1", - "del": "^4.1.1", - "express": "^4.17.1", - "html-entities": "^1.2.1", - "http-proxy-middleware": "0.19.1", - "import-local": "^2.0.0", - "internal-ip": "^4.3.0", - "ip": "^1.1.5", - "is-absolute-url": "^3.0.3", - "killable": "^1.0.1", - "loglevel": "^1.6.6", - "opn": "^5.5.0", - "p-retry": "^3.0.1", - "portfinder": "^1.0.25", - "schema-utils": "^1.0.0", - "selfsigned": "^1.10.7", - "semver": "^6.3.0", - "serve-index": "^1.9.1", - "sockjs": "0.3.19", - "sockjs-client": "1.4.0", - "spdy": "^4.0.1", - "strip-ansi": "^3.0.1", - "supports-color": "^6.1.0", - "url": "^0.11.0", - "webpack-dev-middleware": "^3.7.2", - "webpack-log": "^2.0.0", - "ws": "^6.2.1", - "yargs": "12.0.5" - }, - "bin": { - "webpack-dev-server": "bin/webpack-dev-server.js" - }, - "engines": { - "node": ">= 6.11.5" - } - }, - "node_modules/webpack-dev-server/node_modules/ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack-dev-server/node_modules/anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "dependencies": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "node_modules/webpack-dev-server/node_modules/anymatch/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "dev": true, - "dependencies": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - }, - "optionalDependencies": { - "fsevents": "^1.2.7" - } - }, - "node_modules/webpack-dev-server/node_modules/cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "dev": true, - "dependencies": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - } - }, - "node_modules/webpack-dev-server/node_modules/cliui/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack-dev-server/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents": { - "version": "1.2.12", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz", - "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==", - "bundleDependencies": [ - "abbrev", - "ansi-regex", - "aproba", - "are-we-there-yet", - "balanced-match", - "brace-expansion", - "chownr", - "code-point-at", - "concat-map", - "console-control-strings", - "core-util-is", - "debug", - "deep-extend", - "delegates", - "detect-libc", - "fs-minipass", - "fs.realpath", - "gauge", - "glob", - "has-unicode", - "iconv-lite", - "ignore-walk", - "inflight", - "inherits", - "ini", - "is-fullwidth-code-point", - "isarray", - "minimatch", - "minimist", - "minipass", - "minizlib", - "mkdirp", - "ms", - "needle", - "node-pre-gyp", - "nopt", - "npm-bundled", - "npm-normalize-package-bin", - "npm-packlist", - "npmlog", - "number-is-nan", - "object-assign", - "once", - "os-homedir", - "os-tmpdir", - "osenv", - "path-is-absolute", - "process-nextick-args", - "rc", - "readable-stream", - "rimraf", - "safe-buffer", - "safer-buffer", - "sax", - "semver", - "set-blocking", - "signal-exit", - "string-width", - "string_decoder", - "strip-ansi", - "strip-json-comments", - "tar", - "util-deprecate", - "wide-align", - "wrappy", - "yallist" - ], - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1", - "node-pre-gyp": "*" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "extraneous": true, - "inBundle": true, - "license": "Apache-2.0", - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^2.6.0" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "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" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minimatch": "^3.0.4" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "*" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "minipass": "^2.9.0" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/mkdirp": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", - "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", - "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/needle": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.3.3.tgz", - "integrity": "sha512-EkY0GeSq87rWp1hoq/sH/wnTWgFVhYlnIkbJ0YJFfRgEFlz2RraCjBpFQ+vrEgEdp0ThfyHADmkChEhcb7PKyw==", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - }, - "bin": { - "needle": "bin/needle" - }, - "engines": { - "node": ">= 4.4.x" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/node-pre-gyp": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz", - "integrity": "sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA==", - "extraneous": true, - "inBundle": true, - "license": "BSD-3-Clause", - "dependencies": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4.4.2" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "abbrev": "1", - "osenv": "^0.1.4" - }, - "bin": { - "nopt": "bin/nopt.js" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/npm-bundled": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/npm-packlist": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", - "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "extraneous": true, - "inBundle": true, - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - }, - "engines": { - "node": ">=4.5" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/webpack-dev-server/node_modules/fsevents/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/webpack-dev-server/node_modules/graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, - "node_modules/webpack-dev-server/node_modules/invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack-dev-server/node_modules/is-absolute-url": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", - "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/webpack-dev-server/node_modules/is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "dependencies": { - "binary-extensions": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "dependencies": { - "invert-kv": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-dev-server/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-dev-server/node_modules/os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "dependencies": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-dev-server/node_modules/p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-dev-server/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-dev-server/node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-dev-server/node_modules/readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/webpack-dev-server/node_modules/schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "dependencies": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/webpack-dev-server/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/webpack-dev-server/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-dev-server/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", - "dev": true, - "dependencies": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" - } - }, - "node_modules/webpack-log": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", - "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", - "dev": true, - "dependencies": { - "ansi-colors": "^3.0.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/webpack-log/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true, - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/webpack-merge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", - "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==", - "dev": true, - "dependencies": { - "lodash": "^4.17.15" - } - }, - "node_modules/webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dev": true, - "dependencies": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - }, - "node_modules/webpack-sources/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-subresource-integrity": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.4.0.tgz", - "integrity": "sha512-GB1kB/LwAWC3CxwcedGhMkxGpNZxSheCe1q+KJP1bakuieAdX/rGHEcf5zsEzhKXpqsGqokgsDoD9dIkr61VDQ==", - "dev": true, - "dependencies": { - "webpack-sources": "^1.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack/node_modules/acorn": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", - "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/webpack/node_modules/cacache": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", - "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", - "dev": true, - "dependencies": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - } - }, - "node_modules/webpack/node_modules/find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "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" - }, - "engines": { - "node": "*" - } - }, - "node_modules/webpack/node_modules/graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, - "node_modules/webpack/node_modules/is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/webpack/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/webpack/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/webpack/node_modules/memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dev": true, - "dependencies": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "node_modules/webpack/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/webpack/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/webpack/node_modules/schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "dependencies": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/webpack/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack/node_modules/ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", - "dev": true, - "dependencies": { - "figgy-pudding": "^3.5.1" - } - }, - "node_modules/webpack/node_modules/terser-webpack-plugin": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz", - "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==", - "dev": true, - "dependencies": { - "cacache": "^12.0.2", - "find-cache-dir": "^2.1.0", - "is-wsl": "^1.1.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^2.1.2", - "source-map": "^0.6.1", - "terser": "^4.1.2", - "webpack-sources": "^1.4.0", - "worker-farm": "^1.7.0" - }, - "engines": { - "node": ">= 6.9.0" - } - }, - "node_modules/webpack/node_modules/y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "node_modules/webpack/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/websocket-driver": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz", - "integrity": "sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==", - "dev": true, - "dependencies": { - "http-parser-js": ">=0.4.0 <0.4.11", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/websocket-extensions": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", - "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/when": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/when/-/when-3.6.4.tgz", - "integrity": "sha1-RztRfsFZ4rhQBUl6E5g/CVQS404=", - "dev": true - }, - "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "node_modules/win-fork": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/win-fork/-/win-fork-1.1.1.tgz", - "integrity": "sha1-j1jgZW/KAK3IyGoriePNLWotXl4=", - "bin": { - "win-fork": "bin/win-spawn", - "win-line-endings": "bin/win-line-endings", - "win-spawn": "bin/win-spawn" - } - }, - "node_modules/window-size": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", - "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=", - "bin": { - "window-size": "cli.js" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/worker-farm": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", - "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", - "dev": true, - "dependencies": { - "errno": "~0.1.7" - } - }, - "node_modules/worker-plugin": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/worker-plugin/-/worker-plugin-4.0.2.tgz", - "integrity": "sha512-V+1zSZMOOKk+uBzKyNIODLQLsx59zSIOaI75J1EMS0iR1qy+KQR3y/pQ3T0vIhvPfDFapGRMsoMvQNEL3okqSA==", - "dev": true, - "dependencies": { - "loader-utils": "^1.1.0" - } - }, - "node_modules/worker-plugin/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/worker-plugin/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "dependencies": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "node_modules/write-to": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/write-to/-/write-to-1.1.1.tgz", - "integrity": "sha1-Xu+/aC0SjCqwwF9l3xA4c54GxqM=", - "dependencies": { - "dethroy": "1", - "mkdirp": "0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", - "dev": true, - "dependencies": { - "async-limiter": "~1.0.0" - } - }, - "node_modules/xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", - "dev": true, - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/xmlhttprequest-ssl": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", - "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/xregexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", - "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=" - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/yargs": { - "version": "3.27.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.27.0.tgz", - "integrity": "sha1-ISBUaTFuk5Ex1Z8toMbX+YIh6kA=", - "dependencies": { - "camelcase": "^1.2.1", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "os-locale": "^1.4.0", - "window-size": "^0.1.2", - "y18n": "^3.2.0" - } - }, - "node_modules/yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "node_modules/yargs-parser/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/yeast": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", - "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", - "dev": true - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/zone.js": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.10.3.tgz", - "integrity": "sha512-LXVLVEq0NNOqK/fLJo3d0kfzd4sxwn2/h67/02pjCjfKDxgx1i9QqpvtHD8CrBnSSwMw5+dy11O7FRX5mkO7Cg==" - } - }, - "dependencies": { - "requires": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/requires/-/requires-1.0.2.tgz", - "integrity": "sha1-djBOghNFYi/j+sCwcRoeTygo8Po=" - }, - "@angular-devkit/architect": { - "version": "0.901.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.901.0.tgz", - "integrity": "sha512-SlqEBkPrT40zMCy5344AsUqC76pEPCaGPaAkCIvadaz2dC9vNMzQrvubCPJHViD/TumkSX1kYmLS3iYASVM9GQ==", - "dev": true, - "requires": { - "@angular-devkit/core": "9.1.0", - "rxjs": "6.5.4" - }, - "dependencies": { - "rxjs": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", - "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - } - } - }, - "@angular-devkit/build-angular": { - "version": "0.901.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.901.0.tgz", - "integrity": "sha512-ftJVNlKvIomqRfr5jFVraPqlLSUJu8YyVbFv/aCsvhNpuZGkYpTOMoJDwyywdslSTH608BIoU63IAnIz9PwUdw==", - "dev": true, - "requires": { - "@angular-devkit/architect": "0.901.0", - "@angular-devkit/build-optimizer": "0.901.0", - "@angular-devkit/build-webpack": "0.901.0", - "@angular-devkit/core": "9.1.0", - "@babel/core": "7.9.0", - "@babel/generator": "7.9.3", - "@babel/preset-env": "7.9.0", - "@babel/template": "7.8.6", - "@jsdevtools/coverage-istanbul-loader": "3.0.3", - "@ngtools/webpack": "9.1.0", - "ajv": "6.12.0", - "autoprefixer": "9.7.4", - "babel-loader": "8.0.6", - "browserslist": "^4.9.1", - "cacache": "15.0.0", - "caniuse-lite": "^1.0.30001032", - "circular-dependency-plugin": "5.2.0", - "copy-webpack-plugin": "5.1.1", - "core-js": "3.6.4", - "cssnano": "4.1.10", - "file-loader": "6.0.0", - "find-cache-dir": "3.3.1", - "glob": "7.1.6", - "jest-worker": "25.1.0", - "karma-source-map-support": "1.4.0", - "less": "3.11.1", - "less-loader": "5.0.0", - "license-webpack-plugin": "2.1.4", - "loader-utils": "2.0.0", - "mini-css-extract-plugin": "0.9.0", - "minimatch": "3.0.4", - "open": "7.0.3", - "parse5": "4.0.0", - "postcss": "7.0.27", - "postcss-import": "12.0.1", - "postcss-loader": "3.0.0", - "raw-loader": "4.0.0", - "regenerator-runtime": "0.13.5", - "rimraf": "3.0.2", - "rollup": "2.1.0", - "rxjs": "6.5.4", - "sass": "1.26.3", - "sass-loader": "8.0.2", - "semver": "7.1.3", - "source-map": "0.7.3", - "source-map-loader": "0.2.4", - "source-map-support": "0.5.16", - "speed-measure-webpack-plugin": "1.3.1", - "style-loader": "1.1.3", - "stylus": "0.54.7", - "stylus-loader": "3.0.2", - "terser": "4.6.7", - "terser-webpack-plugin": "2.3.5", - "tree-kill": "1.2.2", - "webpack": "4.42.0", - "webpack-dev-middleware": "3.7.2", - "webpack-dev-server": "3.10.3", - "webpack-merge": "4.2.2", - "webpack-sources": "1.4.3", - "webpack-subresource-integrity": "1.4.0", - "worker-plugin": "4.0.2" - }, - "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "parse5": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", - "dev": true - }, - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "regenerator-runtime": { - "version": "0.13.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", - "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "rxjs": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", - "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "semver": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", - "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", - "dev": true - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@angular-devkit/build-optimizer": { - "version": "0.901.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.901.0.tgz", - "integrity": "sha512-Y9sz8uf2zjilhPUVYb0K9Mio6c1d5c+csuDc15CCKzELXJwyyDxilIFgn6Eu+edM0HNQGzbIwkjy4DkR9mtuTQ==", - "dev": true, - "requires": { - "loader-utils": "2.0.0", - "source-map": "0.7.3", - "tslib": "1.11.1", - "typescript": "3.8.3", - "webpack-sources": "1.4.3" - }, - "dependencies": { - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - }, - "typescript": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", - "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", - "dev": true - } - } - }, - "@angular-devkit/build-webpack": { - "version": "0.901.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.901.0.tgz", - "integrity": "sha512-Oze0VzIvHnoW12C80fiNH4HBu/GWmhJPXdNA7nRkU/tBQlIKnfngf8rQ0QbgecN2qdEXQpZJsP/XclTi3zugsg==", - "dev": true, - "requires": { - "@angular-devkit/architect": "0.901.0", - "@angular-devkit/core": "9.1.0", - "rxjs": "6.5.4" - }, - "dependencies": { - "rxjs": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", - "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - } - } - }, - "@angular-devkit/core": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-9.1.0.tgz", - "integrity": "sha512-vHTsrB4JaVUQ95FRnKrgo79Y3F6FokImrZdrmwkQmwAThpjXeXmpUEKZS+ZSTFRgesjiIysVGOFijARP4BQ7Bg==", - "dev": true, - "requires": { - "ajv": "6.12.0", - "fast-json-stable-stringify": "2.1.0", - "magic-string": "0.25.7", - "rxjs": "6.5.4", - "source-map": "0.7.3" - }, - "dependencies": { - "rxjs": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", - "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - } - } - }, - "@angular-devkit/schematics": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-9.1.0.tgz", - "integrity": "sha512-cb9PSvskMwWlL54fPfCcpJoyNDWAX6Wo7CzL5qpIB2cJCPLAuyfRUYYrkO77YUST+n2HvypHz0cZ5SNGMfaaBQ==", - "dev": true, - "requires": { - "@angular-devkit/core": "9.1.0", - "ora": "4.0.3", - "rxjs": "6.5.4" - }, - "dependencies": { - "rxjs": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", - "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - } - } - }, - "@angular/animations": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-9.1.0.tgz", - "integrity": "sha512-o7X3HM+eocoryw3VrDUtG6Wci2KwtzyBFo3KBJXjQ16X6fwdkjTG+hLb7pp2CBFBEJW4tPYEy7cSBmEfMRTqag==" - }, - "@angular/cdk": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-9.2.0.tgz", - "integrity": "sha512-jeeznvNDpR9POuxzz8Y0zFvMynG9HCJo3ZPTqOjlOq8Lj8876+rLsHDvKEMeLdwlkdi1EweYJW1CLQzI+TwqDA==", - "requires": { - "parse5": "^5.0.0" - } - }, - "@angular/cli": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-9.1.0.tgz", - "integrity": "sha512-ofum4gPE/W3fKyzuJrpdHeOS0ZL8x0eYCgsrMyUoFodSpb5LWPqeW+56NgDTpIeny+Trx3pM9dr9QTUVTJ0vYg==", - "dev": true, - "requires": { - "@angular-devkit/architect": "0.901.0", - "@angular-devkit/core": "9.1.0", - "@angular-devkit/schematics": "9.1.0", - "@schematics/angular": "9.1.0", - "@schematics/update": "0.901.0", - "@yarnpkg/lockfile": "1.1.0", - "ansi-colors": "4.1.1", - "debug": "4.1.1", - "ini": "1.3.5", - "inquirer": "7.1.0", - "npm-package-arg": "8.0.1", - "npm-pick-manifest": "6.0.0", - "open": "7.0.3", - "pacote": "11.1.4", - "read-package-tree": "5.3.1", - "rimraf": "3.0.2", - "semver": "7.1.3", - "symbol-observable": "1.2.0", - "universal-analytics": "0.4.20", - "uuid": "7.0.2" - }, - "dependencies": { - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "semver": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", - "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", - "dev": true - }, - "uuid": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.2.tgz", - "integrity": "sha512-vy9V/+pKG+5ZTYKf+VcphF5Oc6EFiu3W8Nv3P3zIh0EqVI80ZxOzuPfe9EHjkFNvf8+xuTHVeei4Drydlx4zjw==", - "dev": true - } - } - }, - "@angular/common": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-9.1.0.tgz", - "integrity": "sha512-6JPLNtMhI03bGTVQJeSwc+dTjV6DtP7M/BAyzIV0InZP1D6XsOh2QahLFIaaN2sSxYA2ClKuwfX1v+rx9AbXQA==" - }, - "@angular/compiler": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-9.1.0.tgz", - "integrity": "sha512-QHw/JSeTXHiJQ2Ih0EtU7FGsYcOr+0hwZhqwSW3EEn8TtUgA3DS5lXeiDV66f+3DdvNZFPmgiZIvun3ypxn1HA==" - }, - "@angular/compiler-cli": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-9.1.0.tgz", - "integrity": "sha512-xZ8mVPmPporSTtvNA+cbFJQymLzuWfMX6HDDgztZ2eZ5WcQJYloRN4CcYMEzDhCxfV1Zw9Tfc2l14jZD8osi6g==", - "dev": true, - "requires": { - "canonical-path": "1.0.0", - "chokidar": "^3.0.0", - "convert-source-map": "^1.5.1", - "dependency-graph": "^0.7.2", - "fs-extra": "4.0.2", - "magic-string": "^0.25.0", - "minimist": "^1.2.0", - "reflect-metadata": "^0.1.2", - "semver": "^6.3.0", - "source-map": "^0.6.1", - "sourcemap-codec": "^1.4.8", - "yargs": "15.3.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yargs": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.0.tgz", - "integrity": "sha512-g/QCnmjgOl1YJjGsnUg2SatC7NUYEiLXJqxNOQU9qSpjzGtGXda9b+OKccr1kLTy8BN9yqEyqfq5lxlwdc13TA==", - "dev": true, - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.0" - } - }, - "yargs-parser": { - "version": "18.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.2.tgz", - "integrity": "sha512-hlIPNR3IzC1YuL1c2UwwDKpXlNFBqD1Fswwh1khz5+d8Cq/8yc/Mn0i+rQXduu8hcrFKvO7Eryk+09NecTQAAQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "@angular/core": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-9.1.0.tgz", - "integrity": "sha512-RVlyegdIAij0P1wLY5ObIdsBAzvmHkHfElnmfiNKhaDftP6U/3zRtaKDu0bq0jvn1WCQ8zXxFQ8AWyKZwyFS+w==" - }, - "@angular/flex-layout": { - "version": "9.0.0-beta.29", - "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-9.0.0-beta.29.tgz", - "integrity": "sha512-93sxR+kYfYMOdnlWL0Q77FZ428gg8XnBu0YZm6GsCdkw/vLggIT/G1ZAqHlCPIODt6pxmCJ5KXh4ShvniIYDsA==" - }, - "@angular/forms": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-9.1.0.tgz", - "integrity": "sha512-5GC8HQlPChPV+168zLlm4yj4syA6N9ChSKV0tmzj1zIfMcub1UAOaB9IYaXRHQsjPFh9OuQXwmkzScyAfhEVjA==" - }, - "@angular/language-service": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-9.1.0.tgz", - "integrity": "sha512-2f8ECoXrj40oS1rtIfi+F8T4WPzundcZDs8WMFNBuWYbk14v1S9sTgMEmZyePHGkPjt6IfYiLJKJCvVgrt1nxQ==", - "dev": true - }, - "@angular/material": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-9.2.0.tgz", - "integrity": "sha512-KKzEIVh6/m56m+Ao8p4PK0SyEr0574l3VP2swj1qPag3u+FYgemmXCGTaChrKdDsez+zeTCPXImBGXzE6NQ80Q==" - }, - "@angular/platform-browser": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-9.1.0.tgz", - "integrity": "sha512-OsS/blUjl8ranmDaRADjFAmvnlmwbT6WNU7dVov7FhV0rqesbwaOJ5bR0LSYHYpej7Jaa6oYk0v0XWkaH9LTFg==" - }, - "@angular/platform-browser-dynamic": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-9.1.0.tgz", - "integrity": "sha512-sMtz/poQ3TYaWZzWjrn9apKUZ/WKql2MYCWbpax7pql3GgC9OoTslc7ZEe7/d3ynfFE/CQqWBBOuWGD71Z0LMQ==" - }, - "@angular/router": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-9.1.0.tgz", - "integrity": "sha512-cExO1nPnoPFiUJWZ28hTHozPLFoCmqr3xqcM57We0hhKE0esdrO+gRWKRH0EJERukLbU8coPKVhA8daGUpASiQ==" - }, - "@babel/code-frame": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", - "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", - "dev": true, - "requires": { - "@babel/highlight": "^7.8.3" - } - }, - "@babel/compat-data": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.9.0.tgz", - "integrity": "sha512-zeFQrr+284Ekvd9e7KAX954LkapWiOmQtsfHirhxqfdlX6MEC32iRE+pqUGlYIBchdevaCwvzxWGSy/YBNI85g==", - "dev": true, - "requires": { - "browserslist": "^4.9.1", - "invariant": "^2.2.4", - "semver": "^5.5.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "@babel/core": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.0.tgz", - "integrity": "sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.9.0", - "@babel/helper-module-transforms": "^7.9.0", - "@babel/helpers": "^7.9.0", - "@babel/parser": "^7.9.0", - "@babel/template": "^7.8.6", - "@babel/traverse": "^7.9.0", - "@babel/types": "^7.9.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.13", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.9.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.3.tgz", - "integrity": "sha512-RpxM252EYsz9qLUIq6F7YJyK1sv0wWDBFuztfDGWaQKzHjqDHysxSiRUpA/X9jmfqo+WzkAVKFaUily5h+gDCQ==", - "dev": true, - "requires": { - "@babel/types": "^7.9.0", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz", - "integrity": "sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw==", - "dev": true, - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz", - "integrity": "sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw==", - "dev": true, - "requires": { - "@babel/helper-explode-assignable-expression": "^7.8.3", - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.7.tgz", - "integrity": "sha512-4mWm8DCK2LugIS+p1yArqvG1Pf162upsIsjE7cNBjez+NjliQpVhj20obE520nao0o14DaTnFJv+Fw5a0JpoUw==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.8.6", - "browserslist": "^4.9.1", - "invariant": "^2.2.4", - "levenary": "^1.1.1", - "semver": "^5.5.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.8.8", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.8.tgz", - "integrity": "sha512-LYVPdwkrQEiX9+1R29Ld/wTrmQu1SSKYnuOk3g0CkcZMA1p0gsNxJFj/3gBdaJ7Cg0Fnek5z0DsMULePP7Lrqg==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.8.3", - "@babel/helper-regex": "^7.8.3", - "regexpu-core": "^4.7.0" - } - }, - "@babel/helper-define-map": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz", - "integrity": "sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.8.3", - "@babel/types": "^7.8.3", - "lodash": "^4.17.13" - } - }, - "@babel/helper-explode-assignable-expression": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz", - "integrity": "sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw==", - "dev": true, - "requires": { - "@babel/traverse": "^7.8.3", - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-function-name": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", - "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", - "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", - "dev": true, - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz", - "integrity": "sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg==", - "dev": true, - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz", - "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==", - "dev": true, - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-module-imports": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz", - "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==", - "dev": true, - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-module-transforms": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz", - "integrity": "sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.6", - "@babel/helper-simple-access": "^7.8.3", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/template": "^7.8.6", - "@babel/types": "^7.9.0", - "lodash": "^4.17.13" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz", - "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==", - "dev": true, - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - }, - "@babel/helper-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.8.3.tgz", - "integrity": "sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ==", - "dev": true, - "requires": { - "lodash": "^4.17.13" - } - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz", - "integrity": "sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA==", - "dev": true, - "requires": { - "@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": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz", - "integrity": "sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.8.3", - "@babel/helper-optimise-call-expression": "^7.8.3", - "@babel/traverse": "^7.8.6", - "@babel/types": "^7.8.6" - } - }, - "@babel/helper-simple-access": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz", - "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==", - "dev": true, - "requires": { - "@babel/template": "^7.8.3", - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", - "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", - "dev": true, - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz", - "integrity": "sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==", - "dev": true - }, - "@babel/helper-wrap-function": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz", - "integrity": "sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.8.3", - "@babel/types": "^7.8.3" - } - }, - "@babel/helpers": { - "version": "7.9.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.2.tgz", - "integrity": "sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA==", - "dev": true, - "requires": { - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.9.0", - "@babel/types": "^7.9.0" - } - }, - "@babel/highlight": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", - "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.9.0", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.9.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", - "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==", - "dev": true - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz", - "integrity": "sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw==", - "dev": true, - "requires": { - "@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": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz", - "integrity": "sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-dynamic-import": "^7.8.0" - } - }, - "@babel/plugin-proposal-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz", - "integrity": "sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.0" - } - }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" - } - }, - "@babel/plugin-proposal-numeric-separator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz", - "integrity": "sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.0.tgz", - "integrity": "sha512-UgqBv6bjq4fDb8uku9f+wcm1J7YxJ5nT7WO/jBr0cl0PLKb7t1O6RNR1kZbjgx2LQtsDI9hwoQVmn0yhXeQyow==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0" - } - }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" - } - }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz", - "integrity": "sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.0" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.8.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.8.tgz", - "integrity": "sha512-EVhjVsMpbhLw9ZfHWSx2iy13Q8Z/eg8e8ccVWt23sWQK5l1UdkoLJPN5w69UA4uITGBnEZD2JOe4QOHycYKv8A==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.8.8", - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz", - "integrity": "sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz", - "integrity": "sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz", - "integrity": "sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz", - "integrity": "sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ==", - "dev": true, - "requires": { - "@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": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz", - "integrity": "sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz", - "integrity": "sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "lodash": "^4.17.13" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.9.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.2.tgz", - "integrity": "sha512-TC2p3bPzsfvSsqBZo0kJnuelnoK9O3welkUpqSqBQuBF6R5MN2rysopri8kNvtlGIb2jmUO7i15IooAZJjZuMQ==", - "dev": true, - "requires": { - "@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.6", - "@babel/helper-split-export-declaration": "^7.8.3", - "globals": "^11.1.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz", - "integrity": "sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.8.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.8.8.tgz", - "integrity": "sha512-eRJu4Vs2rmttFCdhPUM3bV0Yo/xPSdPw6ML9KHs/bjB4bLA5HXlbvYXPOD5yASodGod+krjYx21xm1QmL8dCJQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz", - "integrity": "sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz", - "integrity": "sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz", - "integrity": "sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ==", - "dev": true, - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.9.0.tgz", - "integrity": "sha512-lTAnWOpMwOXpyDx06N+ywmF3jNbafZEqZ96CGYabxHrxNX8l5ny7dt4bK/rGwAh9utyP2b2Hv7PlZh1AAS54FQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz", - "integrity": "sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz", - "integrity": "sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz", - "integrity": "sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.9.0.tgz", - "integrity": "sha512-vZgDDF003B14O8zJy0XXLnPH4sg+9X5hFBBGN1V+B2rgrB+J2xIypSN6Rk9imB2hSTHQi5OHLrFWsZab1GMk+Q==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.9.0", - "@babel/helper-plugin-utils": "^7.8.3", - "babel-plugin-dynamic-import-node": "^2.3.0" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.0.tgz", - "integrity": "sha512-qzlCrLnKqio4SlgJ6FMMLBe4bySNis8DFn1VkGmOcxG9gqEyPIOzeQrA//u0HAKrWpJlpZbZMPB1n/OPa4+n8g==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.9.0", - "@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": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.9.0.tgz", - "integrity": "sha512-FsiAv/nao/ud2ZWy4wFacoLOm5uxl0ExSQ7ErvP7jpoihLR6Cq90ilOFyX9UXct3rbtKsAiZ9kFt5XGfPe/5SQ==", - "dev": true, - "requires": { - "@babel/helper-hoist-variables": "^7.8.3", - "@babel/helper-module-transforms": "^7.9.0", - "@babel/helper-plugin-utils": "^7.8.3", - "babel-plugin-dynamic-import-node": "^2.3.0" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.9.0.tgz", - "integrity": "sha512-uTWkXkIVtg/JGRSIABdBoMsoIeoHQHPTL0Y2E7xf5Oj7sLqwVsNXOkNk0VJc7vF0IMBsPeikHxFjGe+qmwPtTQ==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.9.0", - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz", - "integrity": "sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.8.3" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz", - "integrity": "sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz", - "integrity": "sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.3" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.9.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.3.tgz", - "integrity": "sha512-fzrQFQhp7mIhOzmOtPiKffvCYQSK10NR8t6BBz2yPbeUHb9OLW8RZGtgDRBn8z2hGcwvKDL3vC7ojPTLNxmqEg==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz", - "integrity": "sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz", - "integrity": "sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA==", - "dev": true, - "requires": { - "regenerator-transform": "^0.14.2" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz", - "integrity": "sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz", - "integrity": "sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz", - "integrity": "sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz", - "integrity": "sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-regex": "^7.8.3" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz", - "integrity": "sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz", - "integrity": "sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz", - "integrity": "sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/preset-env": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.9.0.tgz", - "integrity": "sha512-712DeRXT6dyKAM/FMbQTV/FvRCms2hPCx+3weRjZ8iQVQWZejWWk1wwG6ViWMyqb/ouBbGOl5b6aCk0+j1NmsQ==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.9.0", - "@babel/helper-compilation-targets": "^7.8.7", - "@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-numeric-separator": "^7.8.3", - "@babel/plugin-proposal-object-rest-spread": "^7.9.0", - "@babel/plugin-proposal-optional-catch-binding": "^7.8.3", - "@babel/plugin-proposal-optional-chaining": "^7.9.0", - "@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-numeric-separator": "^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.9.0", - "@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.9.0", - "@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.9.0", - "@babel/plugin-transform-modules-commonjs": "^7.9.0", - "@babel/plugin-transform-modules-systemjs": "^7.9.0", - "@babel/plugin-transform-modules-umd": "^7.9.0", - "@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.7", - "@babel/plugin-transform-property-literals": "^7.8.3", - "@babel/plugin-transform-regenerator": "^7.8.7", - "@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/preset-modules": "^0.1.3", - "@babel/types": "^7.9.0", - "browserslist": "^4.9.1", - "core-js-compat": "^3.6.2", - "invariant": "^2.2.2", - "levenary": "^1.1.1", - "semver": "^5.5.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "@babel/preset-modules": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.3.tgz", - "integrity": "sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/runtime": { - "version": "7.9.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz", - "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.13.4" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.13.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", - "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==", - "dev": true - } - } - }, - "@babel/template": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", - "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/parser": "^7.8.6", - "@babel/types": "^7.8.6" - } - }, - "@babel/traverse": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.0.tgz", - "integrity": "sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.9.0", - "@babel/helper-function-name": "^7.8.3", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/parser": "^7.9.0", - "@babel/types": "^7.9.0", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } - }, - "@babel/types": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.0.tgz", - "integrity": "sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.9.0", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "@istanbuljs/schema": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", - "dev": true - }, - "@jsdevtools/coverage-istanbul-loader": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.3.tgz", - "integrity": "sha512-TAdNkeGB5Fe4Og+ZkAr1Kvn9by2sfL44IAHFtxlh1BA1XJ5cLpO9iSNki5opWESv3l3vSHsZ9BNKuqFKbEbFaA==", - "dev": true, - "requires": { - "convert-source-map": "^1.7.0", - "istanbul-lib-instrument": "^4.0.1", - "loader-utils": "^1.4.0", - "merge-source-map": "^1.1.0", - "schema-utils": "^2.6.4" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - } - } - }, - "@ngtools/webpack": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-9.1.0.tgz", - "integrity": "sha512-kQ+1N/F+5tuUXiiaoqJwhcOIM0I93EEvF3xwpTLRm91wl2i8R1261LvsD/uQPrgLrZNGR6eFhFF1Izn2PnIjQA==", - "dev": true, - "requires": { - "@angular-devkit/core": "9.1.0", - "enhanced-resolve": "4.1.1", - "rxjs": "6.5.4", - "webpack-sources": "1.4.3" - }, - "dependencies": { - "rxjs": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", - "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - } - } - }, - "@npmcli/ci-detect": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@npmcli/ci-detect/-/ci-detect-1.2.0.tgz", - "integrity": "sha512-JtktVH7ASBVIWsQTFlFpeOzhBJskvoBCTfeeRhhZy7ybATcUvwiwotZ8j5rkqUUyB69lIy/AvboiiiGBjYBKBA==", - "dev": true - }, - "@npmcli/git": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.0.1.tgz", - "integrity": "sha512-hVatexiBtx71F01Ars38Hr5AFUGmJgHAfQtRlO5fJlnAawRGSXwEFgjB5i3XdUUmElZU/RXy7fefN02dZKxgPw==", - "dev": true, - "requires": { - "@npmcli/promise-spawn": "^1.1.0", - "mkdirp": "^1.0.3", - "npm-pick-manifest": "^6.0.0", - "promise-inflight": "^1.0.1", - "promise-retry": "^1.1.1", - "unique-filename": "^1.1.1", - "which": "^2.0.2" - }, - "dependencies": { - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "@npmcli/installed-package-contents": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.5.tgz", - "integrity": "sha512-aKIwguaaqb6ViwSOFytniGvLPb9SMCUm39TgM3SfUo7n0TxUMbwoXfpwyvQ4blm10lzbAwTsvjr7QZ85LvTi4A==", - "dev": true, - "requires": { - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1", - "read-package-json-fast": "^1.1.1", - "readdir-scoped-modules": "^1.1.0" - } - }, - "@npmcli/promise-spawn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.1.0.tgz", - "integrity": "sha512-FwbuYN9KXBkloLeIR3xRgI8dyOdfK/KzaJlChszNuwmUXD1lHXfLlSeo4n4KrKt2udIK9K9/TzlnyCA3ubM2fA==", - "dev": true, - "requires": { - "infer-owner": "^1.0.4" - } - }, - "@schematics/angular": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-9.1.0.tgz", - "integrity": "sha512-qkehaITQ1S1udfnnBY5CXGWnk1iVFI8cZayjLUlRfD5w+6v9if3VIuqPssX96MqvkbjyRu1N214+ieaawzLmuA==", - "dev": true, - "requires": { - "@angular-devkit/core": "9.1.0", - "@angular-devkit/schematics": "9.1.0" - } - }, - "@schematics/update": { - "version": "0.901.0", - "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.901.0.tgz", - "integrity": "sha512-u2VESL1dgOSGZK/wcWEz0WcCU/yv764zhzCQerCwUtbV1CISSSDZ6x+prVYDXOdxWBGtDos2MbCF3GEJJI1T+w==", - "dev": true, - "requires": { - "@angular-devkit/core": "9.1.0", - "@angular-devkit/schematics": "9.1.0", - "@yarnpkg/lockfile": "1.1.0", - "ini": "1.3.5", - "npm-package-arg": "^8.0.0", - "pacote": "11.1.4", - "rxjs": "6.5.4", - "semver": "7.1.3", - "semver-intersect": "1.4.0" - }, - "dependencies": { - "rxjs": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", - "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "semver": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", - "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", - "dev": true - } - } - }, - "@tootallnate/once": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.0.0.tgz", - "integrity": "sha512-KYyTT/T6ALPkIRd2Ge080X/BsXvy9O0hcWTtMWkPvwAwF99+vn6Dv4GzrFT/Nn1LePr+FFDbRXXlqmsy9lw2zA==", - "dev": true - }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", - "dev": true - }, - "@types/events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", - "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", - "dev": true - }, - "@types/glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", - "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", - "dev": true, - "requires": { - "@types/events": "*", - "@types/minimatch": "*", - "@types/node": "*" - } - }, - "@types/jasmine": { - "version": "3.5.10", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.5.10.tgz", - "integrity": "sha512-3F8qpwBAiVc5+HPJeXJpbrl+XjawGmciN5LgiO7Gv1pl1RHtjoMNqZpqEksaPJW05ViKe8snYInRs6xB25Xdew==", - "dev": true - }, - "@types/jasminewd2": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.8.tgz", - "integrity": "sha512-d9p31r7Nxk0ZH0U39PTH0hiDlJ+qNVGjlt1ucOoTUptxb2v+Y5VMnsxfwN+i3hK4yQnqBi3FMmoMFcd1JHDxdg==", - "dev": true, - "requires": { - "@types/jasmine": "*" - } - }, - "@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", - "dev": true - }, - "@types/node": { - "version": "12.12.34", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.34.tgz", - "integrity": "sha512-BneGN0J9ke24lBRn44hVHNeDlrXRYF+VRp0HbSUNnEZahXGAysHZIqnf/hER6aabdBgzM4YOV4jrR8gj4Zfi0g==", - "dev": true - }, - "@types/q": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz", - "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==", - "dev": true - }, - "@types/selenium-webdriver": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.17.tgz", - "integrity": "sha512-tGomyEuzSC1H28y2zlW6XPCaDaXFaD6soTdb4GNdmte2qfHtrKqhy0ZFs4r/1hpazCfEZqeTSRLvSasmEx89uw==", - "dev": true - }, - "@types/source-list-map": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", - "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", - "dev": true - }, - "@types/webpack-sources": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.7.tgz", - "integrity": "sha512-XyaHrJILjK1VHVC4aVlKsdNN5KBTwufMb43cQs+flGxtPAf/1Qwl8+Q0tp5BwEGaI8D6XT1L+9bSWXckgkjTLw==", - "dev": true, - "requires": { - "@types/node": "*", - "@types/source-list-map": "*", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "@webassemblyjs/ast": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", - "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==", - "dev": true, - "requires": { - "@webassemblyjs/helper-module-context": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/wast-parser": "1.8.5" - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz", - "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==", - "dev": true - }, - "@webassemblyjs/helper-api-error": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz", - "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==", - "dev": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz", - "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==", - "dev": true - }, - "@webassemblyjs/helper-code-frame": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz", - "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==", - "dev": true, - "requires": { - "@webassemblyjs/wast-printer": "1.8.5" - } - }, - "@webassemblyjs/helper-fsm": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz", - "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==", - "dev": true - }, - "@webassemblyjs/helper-module-context": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz", - "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "mamacro": "^0.0.3" - } - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz", - "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==", - "dev": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz", - "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz", - "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==", - "dev": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz", - "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==", - "dev": true, - "requires": { - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/utf8": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz", - "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==", - "dev": true - }, - "@webassemblyjs/wasm-edit": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz", - "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/helper-wasm-section": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5", - "@webassemblyjs/wasm-opt": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5", - "@webassemblyjs/wast-printer": "1.8.5" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz", - "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/ieee754": "1.8.5", - "@webassemblyjs/leb128": "1.8.5", - "@webassemblyjs/utf8": "1.8.5" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz", - "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz", - "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-api-error": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/ieee754": "1.8.5", - "@webassemblyjs/leb128": "1.8.5", - "@webassemblyjs/utf8": "1.8.5" - } - }, - "@webassemblyjs/wast-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz", - "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/floating-point-hex-parser": "1.8.5", - "@webassemblyjs/helper-api-error": "1.8.5", - "@webassemblyjs/helper-code-frame": "1.8.5", - "@webassemblyjs/helper-fsm": "1.8.5", - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz", - "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/wast-parser": "1.8.5", - "@xtuc/long": "4.2.2" - } - }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "@yarnpkg/lockfile": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", - "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", - "dev": true - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "dev": true, - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "acorn": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", - "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==" - }, - "acorn-node": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", - "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", - "requires": { - "acorn": "^7.0.0", - "acorn-walk": "^7.0.0", - "xtend": "^4.0.2" - } - }, - "acorn-walk": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.1.1.tgz", - "integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==" - }, - "adm-zip": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.14.tgz", - "integrity": "sha512-/9aQCnQHF+0IiCl0qhXoK7qs//SwYE7zX8lsr/DNk1BRAHYxeLZPL4pguwK29gUEqasYQjqPtEpDRSWEkdHn9g==" - }, - "after": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", - "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", - "dev": true - }, - "agent-base": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-1.0.2.tgz", - "integrity": "sha1-aJDT+yFwBLYrcPiSjg+uX4lSpwY=" - }, - "agentkeepalive": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.1.0.tgz", - "integrity": "sha512-CW/n1wxF8RpEuuiq6Vbn9S8m0VSYDMnZESqaJ6F2cWN9fY8rei2qaxweIaRgq+ek8TqfoFIsUjaGNKGGEHElSg==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "depd": "^1.1.2", - "humanize-ms": "^1.2.1" - } - }, - "aggregate-error": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", - "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, - "ajv": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", - "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-errors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", - "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", - "dev": true - }, - "ajv-keywords": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", - "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", - "dev": true - }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" - } - }, - "alphanum-sort": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", - "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", - "dev": true - }, - "alter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/alter/-/alter-0.2.0.tgz", - "integrity": "sha1-x1iICGF1cgNKrmJICvJrHU0cs80=", - "requires": { - "stable": "~0.1.3" - } - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" - }, - "angular-in-memory-web-api": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/angular-in-memory-web-api/-/angular-in-memory-web-api-0.10.0.tgz", - "integrity": "sha512-GzY4lHPR8suREYfgiYiJSBrgq0bdCiFw/LuqW5IsOQfRx98AvS69A5Ehz12oMLy9ABzoGpqmNC5fcFtClDAPqQ==" - }, - "ansi-colors": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", - "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", - "dev": true - }, - "ansi-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", - "dev": true, - "requires": { - "type-fest": "^0.11.0" - } - }, - "ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "app-root-path": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.2.1.tgz", - "integrity": "sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA==", - "dev": true - }, - "append-transform": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", - "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", - "dev": true, - "requires": { - "default-require-extensions": "^2.0.0" - } - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true - }, - "archy": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/archy/-/archy-0.0.2.tgz", - "integrity": "sha1-kQ9Dv2YUH8M1VkWXq8GJ30Sz014=" - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "aria-query": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", - "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=", - "dev": true, - "requires": { - "ast-types-flow": "0.0.7", - "commander": "^2.11.0" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", - "dev": true - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "arraybuffer.slice": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", - "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", - "dev": true - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", - "dev": true - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "assert": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", - "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", - "dev": true, - "requires": { - "object-assign": "^4.1.1", - "util": "0.10.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.1" - } - } - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true - }, - "ast-traverse": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ast-traverse/-/ast-traverse-0.1.1.tgz", - "integrity": "sha1-ac8rg4bxnc2hux4F1o/jWdiJfeY=" - }, - "ast-types": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.3.tgz", - "integrity": "sha512-XTZ7xGML849LkQP86sWdQzfhwbt3YwIO6MqbX9mUNYY98VKaaVZP7YNNm70IpwecbkkxmfC5IYAzOQ/2p29zRA==" - }, - "ast-types-flow": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", - "dev": true - }, - "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } - }, - "async-each": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", - "dev": true - }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true - }, - "autoprefixer": { - "version": "9.7.4", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.4.tgz", - "integrity": "sha512-g0Ya30YrMBAEZk60lp+qfX5YQllG+S5W3GYCFvyHTvhOki0AEQJLPEcIuGRsqVwLi8FvXPVtwTGhfr38hVpm0g==", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "autoprefixer-core": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/autoprefixer-core/-/autoprefixer-core-3.1.2.tgz", - "integrity": "sha1-reXOni2dcbt//DHWlvpeh66+tjQ=", - "requires": { - "caniuse-db": "^1.0.30000006", - "postcss": "~2.2.5" - } - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", - "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", - "dev": true - }, - "axobject-query": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz", - "integrity": "sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww==", - "dev": true, - "requires": { - "ast-types-flow": "0.0.7" - } - }, - "babel-loader": { - "version": "8.0.6", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.6.tgz", - "integrity": "sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw==", - "dev": true, - "requires": { - "find-cache-dir": "^2.0.0", - "loader-utils": "^1.0.2", - "mkdirp": "^0.5.1", - "pify": "^4.0.1" - }, - "dependencies": { - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - } - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - } - } - }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", - "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", - "dev": true, - "requires": { - "object.assign": "^4.1.0" - } - }, - "backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - } - } - }, - "base62": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/base62/-/base62-0.1.1.tgz", - "integrity": "sha1-e0F0wvlESXU7EcJlHAg9qEGnsIQ=" - }, - "base64-arraybuffer": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", - "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", - "dev": true - }, - "base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", - "dev": true - }, - "base64id": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", - "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", - "dev": true - }, - "batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "better-assert": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", - "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", - "dev": true, - "requires": { - "callsite": "1.0.0" - } - }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true - }, - "binary-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", - "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", - "dev": true - }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dev": true, - "optional": true, - "requires": { - "file-uri-to-path": "1.0.0" - }, - "dependencies": { - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "optional": true - } - } - }, - "blob": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", - "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==", - "dev": true - }, - "block-stream": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", - "requires": { - "inherits": "~2.0.0" - } - }, - "blocking-proxy": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz", - "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "dev": true, - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - }, - "dependencies": { - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "dev": true - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "dev": true, - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - } - } - }, - "bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", - "dev": true, - "requires": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", - "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" - } - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "breakable": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/breakable/-/breakable-1.0.0.tgz", - "integrity": "sha1-eEp5eRWjjq0nutRWtVcstLuqeME=" - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, - "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "dev": true, - "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" - } - }, - "browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" - } - }, - "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", - "dev": true, - "requires": { - "bn.js": "^4.1.1", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.2", - "elliptic": "^6.0.0", - "inherits": "^2.0.1", - "parse-asn1": "^5.0.0" - } - }, - "browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "dev": true, - "requires": { - "pako": "~1.0.5" - } - }, - "browserslist": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.11.1.tgz", - "integrity": "sha512-DCTr3kDrKEYNw6Jb9HFxVLQNaue8z+0ZfRBRjmCunKDEXEBajKDj2Y+Uelg+Pi29OnvaSGwjOsnRyNEkXzHg5g==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001038", - "electron-to-chromium": "^1.3.390", - "node-releases": "^1.1.53", - "pkg-up": "^2.0.0" - } - }, - "browserstack": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.3.tgz", - "integrity": "sha512-AO+mECXsW4QcqC9bxwM29O7qWa7bJT94uBFzeb5brylIQwawuEziwq20dPYbins95GlWzOawgyDNdjYAo32EKg==", - "dev": true, - "requires": { - "https-proxy-agent": "^2.2.1" - }, - "dependencies": { - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "dev": true, - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - } - } - } - }, - "buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "dev": true, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - } - } - }, - "buffer-alloc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", - "dev": true, - "requires": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" - } - }, - "buffer-alloc-unsafe": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", - "dev": true - }, - "buffer-fill": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", - "dev": true - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "buffer-indexof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", - "dev": true - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true - }, - "builder-autoprefixer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/builder-autoprefixer/-/builder-autoprefixer-1.0.4.tgz", - "integrity": "sha1-nNjDdqbXoXIAvYyjMbUfIwDhrHA=", - "requires": { - "autoprefixer-core": "^3.1.1" - } - }, - "builder-es6-module-to-cjs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/builder-es6-module-to-cjs/-/builder-es6-module-to-cjs-1.1.0.tgz", - "integrity": "sha1-efMpfRjEe7iLQ5R1OPoccnQnHuM=", - "requires": { - "es6-module-jstransform": "^0.1.2", - "is-module": "^1.0.0" - } - }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", - "dev": true - }, - "builtins": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", - "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=", - "dev": true - }, - "bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", - "integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g=" - }, - "cacache": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.0.tgz", - "integrity": "sha512-L0JpXHhplbJSiDGzyJJnJCTL7er7NzbBgxzVqLswEb4bO91Zbv17OUMuUeu/q0ZwKn3V+1HM4wb9tO4eVE/K8g==", - "dev": true, - "requires": { - "chownr": "^1.1.2", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "infer-owner": "^1.0.4", - "lru-cache": "^5.1.1", - "minipass": "^3.1.1", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", - "move-concurrently": "^1.0.1", - "p-map": "^3.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^2.7.1", - "ssri": "^8.0.0", - "tar": "^6.0.1", - "unique-filename": "^1.1.1" - }, - "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "tar": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.0.1.tgz", - "integrity": "sha512-bKhKrrz2FJJj5s7wynxy/fyxpE0CmCjmOQ1KV4KkgXFWOgoIT/NbTMnB1n+LFNrNk0SSBVGGxcK5AGsyC+pW5Q==", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, - "caller-callsite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", - "dev": true, - "requires": { - "callsites": "^2.0.0" - } - }, - "caller-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", - "dev": true, - "requires": { - "caller-callsite": "^2.0.0" - } - }, - "callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", - "dev": true - }, - "callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", - "dev": true - }, - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" - }, - "caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "caniuse-db": { - "version": "1.0.30001038", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30001038.tgz", - "integrity": "sha512-yeQ2l99M9upOgMIRfZEdes6HuPbQiRZIMBumUwdXeEQz+faSXUZtZ8xeyEdU+TlJckH09M5NtM038sjKsRa2ow==" - }, - "caniuse-lite": { - "version": "1.0.30001038", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001038.tgz", - "integrity": "sha512-zii9quPo96XfOiRD4TrfYGs+QsGZpb2cGiMAzPjtf/hpFgB6zCPZgJb7I1+EATeMw/o+lG8FyRAnI+CWStHcaQ==", - "dev": true - }, - "canonical-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-1.0.0.tgz", - "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chanel": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/chanel/-/chanel-2.2.0.tgz", - "integrity": "sha1-JlKVsouSqoIAX4Zd0VpIlMQ42Tw=", - "requires": { - "co": "*", - "generator-supported": "*", - "to-descriptor": "*" - } - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "chart.js": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.9.3.tgz", - "integrity": "sha512-+2jlOobSk52c1VU6fzkh3UwqHMdSlgH1xFv9FKMqHiNCpXsGPQa/+81AFa+i3jZ253Mq9aAycPwDjnn1XbRNNw==", - "requires": { - "chartjs-color": "^2.1.0", - "moment": "^2.10.2" - } - }, - "chartjs-color": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.4.1.tgz", - "integrity": "sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==", - "requires": { - "chartjs-color-string": "^0.6.0", - "color-convert": "^1.9.3" - } - }, - "chartjs-color-string": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz", - "integrity": "sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==", - "requires": { - "color-name": "^1.0.0" - } - }, - "chokidar": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz", - "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.3.0" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true - }, - "chrome-trace-event": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", - "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "circular-dependency-plugin": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.0.tgz", - "integrity": "sha512-7p4Kn/gffhQaavNfyDFg7LS5S/UT1JAjyGd4UqR2+jzoYF02eDkj0Ec3+48TsIa4zghjLY87nQHIh/ecK9qLdw==", - "dev": true - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-spinners": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.2.0.tgz", - "integrity": "sha512-tgU3fKwzYjiLEQgPMD9Jt+JjHVL9kW93FiIMX/l7rivvOD4/LL0Mf7gda3+4U2KJBloybwgj5KEoQgGRioMiKQ==", - "dev": true - }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "dev": true - }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" - } - }, - "clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", - "dev": true - }, - "clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "dependencies": { - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - } - } - }, - "co": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/co/-/co-3.1.0.tgz", - "integrity": "sha1-TqVOpaCJOBUxheFSEMaNkJK8G3g=" - }, - "coa": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", - "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", - "dev": true, - "requires": { - "@types/q": "^1.5.1", - "chalk": "^2.4.1", - "q": "^1.1.2" - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "codelyzer": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-5.2.2.tgz", - "integrity": "sha512-jB4FZ1Sx7kZhvZVdf+N2BaKTdrrNZOL0Bj10RRfrhHrb3zEvXjJvvq298JPMJAiyiCS/v4zs1QlGU0ip7xGqeA==", - "dev": true, - "requires": { - "app-root-path": "^2.2.1", - "aria-query": "^3.0.0", - "axobject-query": "2.0.2", - "css-selector-tokenizer": "^0.7.1", - "cssauron": "^1.4.0", - "damerau-levenshtein": "^1.0.4", - "semver-dsl": "^1.0.1", - "source-map": "^0.5.7", - "sprintf-js": "^1.1.2" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", - "dev": true - } - } - }, - "cogent": { - "version": "git+ssh://git@github.com/timaschew/cogent.git#2246bd071392f5053a3a110024fd608a40a593ba", - "from": "cogent@git://github.com/timaschew/cogent.git#fix-redirects", - "requires": { - "debug": "*", - "generator-supported": "~0.0.1", - "netrc": "~0.1.3", - "proxy-agent": "^1.1.0", - "raw-body": "^1.1.2", - "statuses": "^1.0.2", - "write-to": "^1.0.0" - } - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz", - "integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==", - "dev": true, - "requires": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - }, - "dependencies": { - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - } - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "color-string": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", - "dev": true, - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", - "dev": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "commoner": { - "version": "0.10.8", - "resolved": "https://registry.npmjs.org/commoner/-/commoner-0.10.8.tgz", - "integrity": "sha1-NPw2cs0kOT6LtH5wyqApOBH08sU=", - "requires": { - "commander": "^2.5.0", - "detective": "^4.3.1", - "glob": "^5.0.15", - "graceful-fs": "^4.1.2", - "iconv-lite": "^0.4.5", - "mkdirp": "^0.5.0", - "private": "^0.1.6", - "q": "^1.1.2", - "recast": "^0.11.17" - }, - "dependencies": { - "ast-types": { - "version": "0.9.6", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.6.tgz", - "integrity": "sha1-ECyenpAF0+fjgpvwxPok7oYu6bk=" - }, - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" - }, - "glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - }, - "recast": { - "version": "0.11.23", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.11.23.tgz", - "integrity": "sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM=", - "requires": { - "ast-types": "0.9.6", - "esprima": "~3.1.0", - "private": "~0.1.5", - "source-map": "~0.5.0" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - } - } - }, - "compare-versions": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", - "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", - "dev": true - }, - "component": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/component/-/component-1.1.0.tgz", - "integrity": "sha1-NgSaold5i37lcUKWHhjgdnkYyao=", - "requires": { - "co": "^3.0.0", - "commander": "^2.2.0", - "component-build": "^1.2.2", - "component-consoler": "^2.0.0", - "component-flatten": "^1.0.1", - "component-ls": "^2.1.0", - "component-outdated2": "^1.0.4", - "component-pin": "^1.0.4", - "component-remotes": "^1.2.0", - "component-resolver": "^1.3.0", - "component-search2": "^1.1.1", - "component-updater": "^1.0.4", - "component-watcher": "^1.0.1", - "debug": "*", - "mkdirp": "~0.3.5", - "rimraf": "^2.2.6", - "semver": "^2.2.1", - "superagent": "~0.17.0", - "tiny-lr-fork": "0.0.5", - "win-fork": "~1.1.1" - } - }, - "component-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", - "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", - "dev": true - }, - "component-build": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/component-build/-/component-build-1.2.2.tgz", - "integrity": "sha1-1bwl0lIE35On2aN6LeAE4RmkpXw=", - "requires": { - "builder-autoprefixer": "^1.0.3", - "builder-es6-module-to-cjs": "^1.1.0", - "component-builder": "^1.1.12", - "debug": "*" - } - }, - "component-builder": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-builder/-/component-builder-1.2.1.tgz", - "integrity": "sha1-o5fBg3na9RH4Woy5TKDZIcHdsaQ=", - "requires": { - "requires": "^1.0.0", - "chanel": "^2.0.0", - "co": "^3.1.0", - "component-flatten": "^1.0.1", - "component-manifest": "^1.0.0", - "component-require2": "^1.0.1", - "cp": "^0.1.1", - "debug": "*", - "generator-supported": "~0.0.1", - "graceful-fs": "^2.0.3", - "mkdirp": "^0.3.5", - "syntax-error": "^1.1.1" - } - }, - "component-consoler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/component-consoler/-/component-consoler-2.0.0.tgz", - "integrity": "sha1-Peq/BGwjm2EGNi7JaJQGNruhTCM=", - "requires": { - "debug": "*" - } - }, - "component-downloader": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/component-downloader/-/component-downloader-1.2.0.tgz", - "integrity": "sha1-b/Z/9xyEaoWKrjGog1X5vxrUO20=", - "requires": { - "chanel": "^2.0.1", - "co": "^3", - "component-consoler": "^2.0.0", - "component-remotes": "^1.1.1", - "debug": "*", - "decompress": "~0.2.0", - "generator-supported": "~0.0.1", - "graceful-fs": "^2.0.2", - "mkdirp": "~0.3.5", - "rimraf": "^2.2.6", - "semver": "^2.2.0", - "unglob": "~0.1.2", - "write-to": "^1.0.0" - } - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "component-flatten": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/component-flatten/-/component-flatten-1.0.1.tgz", - "integrity": "sha1-W0n0msRcuIxDAyuXozb1P4fNbIw=", - "requires": { - "semver": "^2.2.1" - } - }, - "component-inherit": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", - "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", - "dev": true - }, - "component-ls": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/component-ls/-/component-ls-2.1.0.tgz", - "integrity": "sha1-wR7ez4mDbd6JBkrQT+JK3YClERI=", - "requires": { - "archy": "~0.0.2" - } - }, - "component-manifest": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/component-manifest/-/component-manifest-1.0.0.tgz", - "integrity": "sha1-SNy7HqPGiHYifmra/PNxJbC81XI=", - "requires": { - "debug": "*", - "generator-supported": "~0.0.1", - "graceful-fs": "^2.0.1", - "unglob": "~0.1.1" - } - }, - "component-outdated2": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/component-outdated2/-/component-outdated2-1.0.5.tgz", - "integrity": "sha1-IGbA9Cx1w18tS/3iv5/s4M5GPRQ=", - "requires": { - "component-consoler": "^2.0.0", - "component-flatten": "^1.0.1", - "component-remotes": "^1.1.5", - "generator-supported": "~0.0.1", - "semver": "^2.2.1" - } - }, - "component-pin": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/component-pin/-/component-pin-1.0.5.tgz", - "integrity": "sha1-v6lOAepCB18ufU17X3i2Yt24NY0=", - "requires": { - "component-consoler": "^2.0.0", - "component-flatten": "^1.0.1", - "component-remotes": "^1.1.5", - "generator-supported": "~0.0.1", - "semver": "^2.2.1" - } - }, - "component-remotes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/component-remotes/-/component-remotes-1.2.0.tgz", - "integrity": "sha1-PQeYpR5ziZxs5VnZN2Olb8eh2IM=", - "requires": { - "co": "^3", - "cogent": "git://github.com/timaschew/cogent.git#fix-redirects", - "component-consoler": "^2.0.0", - "component-validator": "^1.0.0", - "debug": "*", - "generator-supported": "~0.0.1", - "graceful-fs": "^2.0.2", - "semver": "^2.2.1" - } - }, - "component-require2": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/component-require2/-/component-require2-1.1.1.tgz", - "integrity": "sha1-POrqGYc8HG74X1O48572koWvXMU=" - }, - "component-resolver": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-resolver/-/component-resolver-1.3.0.tgz", - "integrity": "sha1-79grBa9krRJ4W5h8AfCsQfAKaNw=", - "requires": { - "chanel": "^2.0.2", - "co": "^3", - "component-consoler": "^2.0.0", - "component-downloader": "^1.2.0", - "component-flatten": "^1.0.1", - "component-remotes": "^1.2.0", - "component-validator": "^1.1.1", - "debug": "*", - "generator-supported": "~0.0.1", - "graceful-fs": "^2.0.1", - "semver": "^2.2.1", - "unglob": "~0.1.2" - } - }, - "component-search2": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/component-search2/-/component-search2-1.1.1.tgz", - "integrity": "sha1-kmeDygb2Hvdd7YW7ZVuq3i/J65E=", - "requires": { - "bytes": "~0.3.0", - "cogent": "~0.4.0", - "component-consoler": "^2.0.0", - "debug": "*", - "event-stream": "^3.1.1", - "generator-supported": "~0.0.1", - "JSONStream": "~0.8.0", - "stream-to-array": "^1.0.0" - }, - "dependencies": { - "bytes": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-0.3.0.tgz", - "integrity": "sha1-eOLg4ox/nHuYjqiu4NtNX6mUGTU=" - }, - "cogent": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/cogent/-/cogent-0.4.3.tgz", - "integrity": "sha1-UAdE8R2D6cD8Q+9vlem5zHFaCFQ=", - "requires": { - "debug": "*", - "generator-supported": "~0.0.1", - "netrc": "~0.1.3", - "proxy-agent": "^1.1.0", - "raw-body": "^1.1.2", - "statuses": "^1.0.2", - "write-to": "^1.0.0" - } - } - } - }, - "component-updater": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/component-updater/-/component-updater-1.0.5.tgz", - "integrity": "sha1-CHd/ZxzodeE+MHwdghfC6l8aSsQ=", - "requires": { - "component-consoler": "^2.0.0", - "component-flatten": "^1.0.1", - "component-remotes": "^1.1.5", - "generator-supported": "~0.0.1", - "semver": "^2.2.1" - } - }, - "component-validator": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/component-validator/-/component-validator-1.1.1.tgz", - "integrity": "sha1-+lD+t/WL7IndaXktE6Mn4lJgjl4=", - "requires": { - "component-consoler": "^2.0.0" - } - }, - "component-watcher": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/component-watcher/-/component-watcher-1.0.3.tgz", - "integrity": "sha1-wl6AF0sLQFVJXX7xJnmGJm2QxTU=", - "requires": { - "debug": "*", - "sane": "0" - } - }, - "compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dev": true, - "requires": { - "mime-db": ">= 1.43.0 < 2" - } - }, - "compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dev": true, - "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "dependencies": { - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "connect": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", - "dev": true, - "requires": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", - "dev": true - }, - "console-browserify": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", - "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", - "dev": true - }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true - }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "dev": true, - "requires": { - "safe-buffer": "5.1.2" - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true - }, - "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", - "dev": true - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", - "dev": true - }, - "cookiejar": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-1.3.0.tgz", - "integrity": "sha1-3QCzVnkCHpnL1OhVua0EGRNHR2U=" - }, - "copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", - "dev": true, - "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - } - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "copy-webpack-plugin": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.1.1.tgz", - "integrity": "sha512-P15M5ZC8dyCjQHWwd4Ia/dm0SgVvZJMYeykVIVYXbGyqO4dWB5oyPHp9i7wjwo5LhtlhKbiBCdS2NvM07Wlybg==", - "dev": true, - "requires": { - "cacache": "^12.0.3", - "find-cache-dir": "^2.1.0", - "glob-parent": "^3.1.0", - "globby": "^7.1.1", - "is-glob": "^4.0.1", - "loader-utils": "^1.2.3", - "minimatch": "^3.0.4", - "normalize-path": "^3.0.0", - "p-limit": "^2.2.1", - "schema-utils": "^1.0.0", - "serialize-javascript": "^2.1.2", - "webpack-log": "^2.0.0" - }, - "dependencies": { - "cacache": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", - "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", - "dev": true, - "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - } - }, - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - }, - "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1" - } - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, - "core-js": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz", - "integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==", - "dev": true - }, - "core-js-compat": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.4.tgz", - "integrity": "sha512-zAa3IZPvsJ0slViBQ2z+vgyyTuhd3MFn1rBQjZSKVEgB0UMYhUkCj9jJUVPgGTGqWvsBVmfnruXgTcNyTlEiSA==", - "dev": true, - "requires": { - "browserslist": "^4.8.3", - "semver": "7.0.0" - }, - "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true - } - } - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "cosmiconfig": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", - "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", - "dev": true, - "requires": { - "import-fresh": "^2.0.0", - "is-directory": "^0.3.1", - "js-yaml": "^3.13.1", - "parse-json": "^4.0.0" - } - }, - "cp": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/cp/-/cp-0.1.1.tgz", - "integrity": "sha1-OUanbBpT/+DmhZPzQcEkszbB8G0=" - }, - "create-ecdh": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", - "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.0.0" - } - }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, - "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" - } - }, - "css": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", - "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "source-map": "^0.6.1", - "source-map-resolve": "^0.5.2", - "urix": "^0.1.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "css-color-names": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", - "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", - "dev": true - }, - "css-declaration-sorter": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", - "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", - "dev": true, - "requires": { - "postcss": "^7.0.1", - "timsort": "^0.3.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "css-parse": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz", - "integrity": "sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q=", - "dev": true, - "requires": { - "css": "^2.0.0" - } - }, - "css-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", - "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", - "dev": true, - "requires": { - "boolbase": "^1.0.0", - "css-what": "^3.2.1", - "domutils": "^1.7.0", - "nth-check": "^1.0.2" - } - }, - "css-select-base-adapter": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", - "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", - "dev": true - }, - "css-selector-tokenizer": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.2.tgz", - "integrity": "sha512-yj856NGuAymN6r8bn8/Jl46pR+OC3eEvAhfGYDUe7YPtTPAYrSSw4oAniZ9Y8T5B92hjhwTBLUen0/vKPxf6pw==", - "dev": true, - "requires": { - "cssesc": "^3.0.0", - "fastparse": "^1.1.2", - "regexpu-core": "^4.6.0" - } - }, - "css-tree": { - "version": "1.0.0-alpha.37", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", - "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", - "dev": true, - "requires": { - "mdn-data": "2.0.4", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "css-what": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.2.1.tgz", - "integrity": "sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw==", - "dev": true - }, - "cssauron": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz", - "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=", - "dev": true, - "requires": { - "through": "X.X.X" - } - }, - "cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true - }, - "cssnano": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz", - "integrity": "sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==", - "dev": true, - "requires": { - "cosmiconfig": "^5.0.0", - "cssnano-preset-default": "^4.0.7", - "is-resolvable": "^1.0.0", - "postcss": "^7.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "cssnano-preset-default": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz", - "integrity": "sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==", - "dev": true, - "requires": { - "css-declaration-sorter": "^4.0.1", - "cssnano-util-raw-cache": "^4.0.1", - "postcss": "^7.0.0", - "postcss-calc": "^7.0.1", - "postcss-colormin": "^4.0.3", - "postcss-convert-values": "^4.0.1", - "postcss-discard-comments": "^4.0.2", - "postcss-discard-duplicates": "^4.0.2", - "postcss-discard-empty": "^4.0.1", - "postcss-discard-overridden": "^4.0.1", - "postcss-merge-longhand": "^4.0.11", - "postcss-merge-rules": "^4.0.3", - "postcss-minify-font-values": "^4.0.2", - "postcss-minify-gradients": "^4.0.2", - "postcss-minify-params": "^4.0.2", - "postcss-minify-selectors": "^4.0.2", - "postcss-normalize-charset": "^4.0.1", - "postcss-normalize-display-values": "^4.0.2", - "postcss-normalize-positions": "^4.0.2", - "postcss-normalize-repeat-style": "^4.0.2", - "postcss-normalize-string": "^4.0.2", - "postcss-normalize-timing-functions": "^4.0.2", - "postcss-normalize-unicode": "^4.0.1", - "postcss-normalize-url": "^4.0.1", - "postcss-normalize-whitespace": "^4.0.2", - "postcss-ordered-values": "^4.1.2", - "postcss-reduce-initial": "^4.0.3", - "postcss-reduce-transforms": "^4.0.2", - "postcss-svgo": "^4.0.2", - "postcss-unique-selectors": "^4.0.1" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "cssnano-util-get-arguments": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", - "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=", - "dev": true - }, - "cssnano-util-get-match": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", - "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=", - "dev": true - }, - "cssnano-util-raw-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", - "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "cssnano-util-same-parent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", - "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", - "dev": true - }, - "csso": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.0.3.tgz", - "integrity": "sha512-NL3spysxUkcrOgnpsT4Xdl2aiEiBG6bXswAABQVHcMrfjjBisFOKwLDOmf4wf32aPdcJws1zds2B0Rg+jqMyHQ==", - "dev": true, - "requires": { - "css-tree": "1.0.0-alpha.39" - }, - "dependencies": { - "css-tree": { - "version": "1.0.0-alpha.39", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.39.tgz", - "integrity": "sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA==", - "dev": true, - "requires": { - "mdn-data": "2.0.6", - "source-map": "^0.6.1" - } - }, - "mdn-data": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.6.tgz", - "integrity": "sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "custom-event": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", - "dev": true - }, - "cyclist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", - "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", - "dev": true - }, - "damerau-levenshtein": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz", - "integrity": "sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug==", - "dev": true - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "data-uri-to-buffer": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-0.0.4.tgz", - "integrity": "sha1-RuE6udqOMJdFyNAc5UchPr2y/j8=" - }, - "date-format": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz", - "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==", - "dev": true - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "debuglog": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", - "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=", - "dev": true - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true - }, - "decompress": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/decompress/-/decompress-0.2.5.tgz", - "integrity": "sha1-0hMjPv4GbM2A2RTXk/GzDNmEuEc=", - "requires": { - "adm-zip": "^0.4.3", - "ext-name": "^1.0.0", - "get-stdin": "^0.1.0", - "mkdirp": "^0.3.5", - "nopt": "^2.2.0", - "rimraf": "^2.2.2", - "stream-combiner": "^0.0.4", - "tar": "^0.1.18", - "tempfile": "^0.1.2" - } - }, - "deep-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", - "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", - "dev": true, - "requires": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" - } - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" - }, - "default-gateway": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", - "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "ip-regex": "^2.1.0" - } - }, - "default-require-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", - "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", - "dev": true, - "requires": { - "strip-bom": "^3.0.0" - } - }, - "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", - "dev": true, - "requires": { - "clone": "^1.0.2" - }, - "dependencies": { - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true - } - } - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - } - } - }, - "defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" - }, - "defs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/defs/-/defs-1.1.1.tgz", - "integrity": "sha1-siYJ8sehG6ej2xFoBcE5scr/qdI=", - "requires": { - "alter": "~0.2.0", - "ast-traverse": "~0.1.1", - "breakable": "~1.0.0", - "esprima-fb": "~15001.1001.0-dev-harmony-fb", - "simple-fmt": "~0.1.0", - "simple-is": "~0.2.0", - "stringmap": "~0.2.2", - "stringset": "~0.2.1", - "tryor": "~0.1.2", - "yargs": "~3.27.0" - }, - "dependencies": { - "esprima-fb": { - "version": "15001.1001.0-dev-harmony-fb", - "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz", - "integrity": "sha1-Q761fsJujPI3092LM+QlM1d/Jlk=" - } - } - }, - "degenerator": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-1.0.4.tgz", - "integrity": "sha1-/PSQo37OJmRk2cxDGrmMWBnO0JU=", - "requires": { - "ast-types": "0.x.x", - "escodegen": "1.x.x", - "esprima": "3.x.x" - }, - "dependencies": { - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" - } - } - }, - "del": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", - "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", - "dev": true, - "requires": { - "@types/glob": "^7.1.1", - "globby": "^6.1.0", - "is-path-cwd": "^2.0.0", - "is-path-in-cwd": "^2.0.0", - "p-map": "^2.0.0", - "pify": "^4.0.1", - "rimraf": "^2.6.3" - }, - "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "dev": true - } - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true - }, - "dependency-graph": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.7.2.tgz", - "integrity": "sha512-KqtH4/EZdtdfWX0p6MGP9jljvxSY6msy/pRUD4jgNwVpv3v1QmNLlsB3LDSSUg79BRVSn7jI1QPRtArGABovAQ==", - "dev": true - }, - "des.js": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", - "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", - "dev": true - }, - "detect-node": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", - "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", - "dev": true - }, - "detective": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/detective/-/detective-4.7.1.tgz", - "integrity": "sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig==", - "requires": { - "acorn": "^5.2.1", - "defined": "^1.0.0" - }, - "dependencies": { - "acorn": { - "version": "5.7.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", - "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==" - } - } - }, - "dethroy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/dethroy/-/dethroy-1.0.2.tgz", - "integrity": "sha1-yCADMXaT3LzaP3UrAYLjvxbdRCE=" - }, - "dezalgo": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", - "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=", - "dev": true, - "requires": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "di": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", - "dev": true - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" - } - }, - "dir-glob": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", - "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", - "dev": true, - "requires": { - "path-type": "^3.0.0" - } - }, - "dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", - "dev": true - }, - "dns-packet": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", - "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", - "dev": true, - "requires": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" - } - }, - "dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", - "dev": true, - "requires": { - "buffer-indexof": "^1.0.0" - } - }, - "dom-serialize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", - "dev": true, - "requires": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" - } - }, - "dom-serializer": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", - "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "entities": "^2.0.0" - }, - "dependencies": { - "domelementtype": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", - "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==", - "dev": true - } - } - }, - "domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", - "dev": true - }, - "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", - "dev": true - }, - "domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", - "dev": true, - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "dot-prop": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", - "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", - "dev": true, - "requires": { - "is-obj": "^2.0.0" - } - }, - "duplexer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", - "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=" - }, - "duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "dev": true, - "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - } - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "dev": true - }, - "electron-to-chromium": { - "version": "1.3.395", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.395.tgz", - "integrity": "sha512-kdn2cX6hZXDdz/O2Q8tZscITlsSv1a/7bOq/fQs7QAJ9iaRlnhZPccarNhxZv1tXgmgwCnKp/1lJNYLOG8Dxiw==", - "dev": true - }, - "elliptic": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", - "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", - "dev": true, - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" - } - }, - "emitter-component": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.0.0.tgz", - "integrity": "sha1-8E3Rj8PcPpp0y8DzELCIZm5MAW8=" - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true - }, - "encoding": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", - "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", - "dev": true, - "optional": true, - "requires": { - "iconv-lite": "~0.4.13" - } - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "engine.io": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz", - "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==", - "dev": true, - "requires": { - "accepts": "~1.3.4", - "base64id": "1.0.0", - "cookie": "0.3.1", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.0", - "ws": "~3.3.1" - }, - "dependencies": { - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", - "dev": true - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "ws": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" - } - } - } - }, - "engine.io-client": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", - "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==", - "dev": true, - "requires": { - "component-emitter": "1.2.1", - "component-inherit": "0.0.3", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.1", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "ws": "~3.3.1", - "xmlhttprequest-ssl": "~1.5.4", - "yeast": "0.1.2" - }, - "dependencies": { - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "ws": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" - } - } - } - }, - "engine.io-parser": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz", - "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==", - "dev": true, - "requires": { - "after": "0.8.2", - "arraybuffer.slice": "~0.0.7", - "base64-arraybuffer": "0.1.5", - "blob": "0.0.5", - "has-binary2": "~1.0.2" - } - }, - "enhanced-resolve": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz", - "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.5.0", - "tapable": "^1.0.0" - }, - "dependencies": { - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - } - } - }, - "ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", - "dev": true - }, - "entities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", - "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==", - "dev": true - }, - "err-code": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", - "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=", - "dev": true - }, - "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "dev": true, - "requires": { - "prr": "~1.0.1" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es6-module-jstransform": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/es6-module-jstransform/-/es6-module-jstransform-0.1.4.tgz", - "integrity": "sha1-OtHj6qt0UY9a8jwQFPGCjF6WfAI=", - "requires": { - "esprima-fb": "~3001.1.0-dev-harmony-fb", - "jstransform": "~3.0.0" - } - }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", - "dev": true - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", - "dev": true, - "requires": { - "es6-promise": "^4.0.3" - } - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "escodegen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz", - "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==", - "requires": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - }, - "dependencies": { - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "optional": true - } - } - }, - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "esprima-fb": { - "version": "3001.1.0-dev-harmony-fb", - "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-3001.0001.0000-dev-harmony-fb.tgz", - "integrity": "sha1-t303q8046gt3Qmu4vCkizmtCZBE=" - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "^4.1.0" - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "dev": true - }, - "event-stream": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.5.tgz", - "integrity": "sha512-vyibDcu5JL20Me1fP734QBH/kenBGLZap2n0+XXM7mvuUPzJ20Ydqj1aKcIeMdri1p+PU+4yAKugjN8KCVst+g==", - "requires": { - "duplexer": "^0.1.1", - "from": "^0.1.7", - "map-stream": "0.0.7", - "pause-stream": "^0.0.11", - "split": "^1.0.1", - "stream-combiner": "^0.2.2", - "through": "^2.3.8" - }, - "dependencies": { - "stream-combiner": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", - "integrity": "sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg=", - "requires": { - "duplexer": "~0.1.1", - "through": "~2.3.4" - } - } - } - }, - "eventemitter3": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", - "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==", - "dev": true - }, - "events": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", - "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==", - "dev": true - }, - "eventsource": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", - "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", - "dev": true, - "requires": { - "original": "^1.0.0" - } - }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.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" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "dev": true, - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "dev": true - } - } - }, - "ext-list": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-0.2.0.tgz", - "integrity": "sha1-NhTV8pn0pZKolinn3oJfF3TRmr0=", - "requires": { - "got": "^0.2.0" - } - }, - "ext-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-1.0.1.tgz", - "integrity": "sha1-GCgzVtxAo5NFXFRGDwWZzpfTDgw=", - "requires": { - "ext-list": "^0.2.0", - "underscore.string": "~2.3.3" - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - } - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, - "fast-deep-equal": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" - }, - "fastparse": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", - "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", - "dev": true - }, - "faye-websocket": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.4.4.tgz", - "integrity": "sha1-wUxbO/FNdBf/v9mQwKdJXNnzN7w=" - }, - "figgy-pudding": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", - "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", - "dev": true - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-loader": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.0.0.tgz", - "integrity": "sha512-/aMOAYEFXDdjG0wytpTL5YQLfZnnTmLNjn+AIrJ/6HVnTfDqLsVKUUwkDf4I4kgex36BvjuXEn/TX9B/1ESyqQ==", - "dev": true, - "requires": { - "loader-utils": "^2.0.0", - "schema-utils": "^2.6.5" - } - }, - "file-uri-to-path": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-0.0.2.tgz", - "integrity": "sha1-N83RtbkFQEs/BeGyNkW+aU/3D4I=" - }, - "fileset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", - "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", - "dev": true, - "requires": { - "glob": "^7.0.3", - "minimatch": "^3.0.3" - }, - "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "find-cache-dir": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", - "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "make-dir": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", - "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", - "dev": true - }, - "flush-write-stream": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "readable-stream": "^2.3.6" - } - }, - "follow-redirects": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.11.0.tgz", - "integrity": "sha512-KZm0V+ll8PfBrKwMzdo5D13b1bur9Iq9Zd/RMmAoQQcl2PxxFml8cxXPaaPYVbV0RjNjq1CU7zIzAOqtUPudmA==", - "dev": true, - "requires": { - "debug": "^3.0.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "formidable": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.14.tgz", - "integrity": "sha1-Kz9MQRy7X91pXESEPiojUUpDIxo=" - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "dev": true - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "dev": true - }, - "from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=" - }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "fs-extra": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.2.tgz", - "integrity": "sha1-+RcExT0bRh+JNFKwwwfZmXZHq2s=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "dependencies": { - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - } - } - }, - "fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" - }, - "dependencies": { - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - } - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "fsevents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", - "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", - "dev": true, - "optional": true - }, - "fstream": { - "version": "0.1.31", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-0.1.31.tgz", - "integrity": "sha1-czfwWPu7vvqMn1YaKMqwhJICyYg=", - "requires": { - "graceful-fs": "~3.0.2", - "inherits": "~2.0.0", - "mkdirp": "0.5", - "rimraf": "2" - }, - "dependencies": { - "graceful-fs": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.12.tgz", - "integrity": "sha512-J55gaCS4iTTJfTXIxSVw3EMQckcqkpdRv3IR7gu6sq0+tbC363Zx6KH/SEwXASK9JRbhyZmVjJEVJIOxYsB3Qg==", - "requires": { - "natives": "^1.1.3" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - } - } - }, - "ftp": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", - "integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=", - "requires": { - "readable-stream": "1.1.x", - "xregexp": "2.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - } - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "generator-supported": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/generator-supported/-/generator-supported-0.0.1.tgz", - "integrity": "sha1-kivSIBpsONj6y4FdWT2KA8iYwpk=" - }, - "gensync": { - "version": "1.0.0-beta.1", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", - "dev": true - }, - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "dev": true - }, - "get-stdin": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-0.1.0.tgz", - "integrity": "sha1-WZivJKr8gC0VyCxoVlfuuLENSpE=" - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "get-uri": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-0.1.4.tgz", - "integrity": "sha1-NfinlUwSn7Ey/y3fXtgaV8uKnlQ=", - "requires": { - "data-uri-to-buffer": "0", - "debug": "2", - "extend": "3", - "file-uri-to-path": "0", - "ftp": "~0.3.5", - "readable-stream": "2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", - "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", - "requires": { - "inherits": "2", - "minimatch": "0.3" - }, - "dependencies": { - "minimatch": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", - "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", - "requires": { - "lru-cache": "2", - "sigmund": "~1.0.0" - } - } - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "globby": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", - "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "dir-glob": "^2.0.0", - "glob": "^7.1.2", - "ignore": "^3.3.5", - "pify": "^3.0.0", - "slash": "^1.0.0" - }, - "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "got": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/got/-/got-0.2.0.tgz", - "integrity": "sha1-0Awkiyn9zK6pQN+coJlev/MbUaU=", - "requires": { - "object-assign": "^0.3.0" - } - }, - "graceful-fs": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz", - "integrity": "sha1-fNLNsiiko/Nule+mzBQt59GhNtA=" - }, - "handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", - "dev": true - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "dev": true, - "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "has-binary2": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", - "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", - "dev": true, - "requires": { - "isarray": "2.0.1" - }, - "dependencies": { - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", - "dev": true - } - } - }, - "has-cors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "hex-color-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", - "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", - "dev": true - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "hosted-git-info": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.4.tgz", - "integrity": "sha512-4oT62d2jwSDBbLLFLZE+1vPuQ1h8p9wjrJ8Mqx5TjsyWmBMV5B13eJqn8pvluqubLf3cJPTfiYCIwNwDNmzScQ==", - "dev": true, - "requires": { - "lru-cache": "^5.1.1" - }, - "dependencies": { - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, - "hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "hsl-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", - "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=", - "dev": true - }, - "hsla-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", - "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=", - "dev": true - }, - "html-comment-regex": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", - "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", - "dev": true - }, - "html-entities": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", - "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", - "dev": true - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", - "dev": true - }, - "http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", - "dev": true - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - } - } - }, - "http-parser-js": { - "version": "0.4.10", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz", - "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=", - "dev": true - }, - "http-proxy": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz", - "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==", - "dev": true, - "requires": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - } - }, - "http-proxy-agent": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-0.2.7.tgz", - "integrity": "sha1-4X/aZfCQLZUs55IeYsf/iGJlWl4=", - "requires": { - "agent-base": "~1.0.1", - "debug": "2", - "extend": "3" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "http-proxy-middleware": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", - "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", - "dev": true, - "requires": { - "http-proxy": "^1.17.0", - "is-glob": "^4.0.0", - "lodash": "^4.17.11", - "micromatch": "^3.1.10" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", - "dev": true - }, - "https-proxy-agent": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-0.3.6.tgz", - "integrity": "sha1-cT+jjl01P1DrFKNC/r4pAz7RYZs=", - "requires": { - "agent-base": "~1.0.1", - "debug": "2", - "extend": "3" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", - "dev": true, - "requires": { - "ms": "^2.0.0" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "dev": true - }, - "iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", - "dev": true - }, - "ignore": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", - "dev": true - }, - "ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "dev": true, - "requires": { - "minimatch": "^3.0.4" - }, - "dependencies": { - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "image-size": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", - "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", - "dev": true, - "optional": true - }, - "immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", - "dev": true - }, - "import-cwd": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", - "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", - "dev": true, - "requires": { - "import-from": "^2.1.0" - } - }, - "import-fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", - "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", - "dev": true, - "requires": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" - } - }, - "import-from": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", - "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", - "dev": true, - "requires": { - "resolve-from": "^3.0.0" - } - }, - "import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", - "dev": true, - "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "indexes-of": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", - "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", - "dev": true - }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true - }, - "infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true - }, - "inquirer": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz", - "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^3.0.0", - "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.4.0", - "rxjs": "^6.5.3", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "internal-ip": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", - "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", - "dev": true, - "requires": { - "default-gateway": "^4.2.0", - "ipaddr.js": "^1.9.0" - } - }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "requires": { - "loose-envify": "^1.0.0" - } - }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" - }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" - }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true - }, - "is-absolute-url": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", - "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", - "dev": true - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "is-arguments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", - "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", - "dev": true - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", - "dev": true - }, - "is-color-stop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", - "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", - "dev": true, - "requires": { - "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": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", - "dev": true - }, - "is-docker": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz", - "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==", - "dev": true - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true - }, - "is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=", - "dev": true - }, - "is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=" - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true - }, - "is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "dev": true - }, - "is-path-in-cwd": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", - "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", - "dev": true, - "requires": { - "is-path-inside": "^2.1.0" - } - }, - "is-path-inside": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", - "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", - "dev": true, - "requires": { - "path-is-inside": "^1.0.2" - } - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", - "dev": true - }, - "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-svg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", - "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", - "dev": true, - "requires": { - "html-comment-regex": "^1.1.0" - } - }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "is-wsl": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.1.1.tgz", - "integrity": "sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog==", - "dev": true - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "isbinaryfile": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", - "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", - "dev": true, - "requires": { - "buffer-alloc": "^1.2.0" - } - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "istanbul-api": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.6.tgz", - "integrity": "sha512-x0Eicp6KsShG1k1rMgBAi/1GgY7kFGEBwQpw3PXGEmu+rBcBNhqU8g2DgY9mlepAsLPzrzrbqSgCGANnki4POA==", - "dev": true, - "requires": { - "async": "^2.6.2", - "compare-versions": "^3.4.0", - "fileset": "^2.0.3", - "istanbul-lib-coverage": "^2.0.5", - "istanbul-lib-hook": "^2.0.7", - "istanbul-lib-instrument": "^3.3.0", - "istanbul-lib-report": "^2.0.8", - "istanbul-lib-source-maps": "^3.0.6", - "istanbul-reports": "^2.2.4", - "js-yaml": "^3.13.1", - "make-dir": "^2.1.0", - "minimatch": "^3.0.4", - "once": "^1.4.0" - }, - "dependencies": { - "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", - "dev": true, - "requires": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", - "dev": true - }, - "istanbul-lib-hook": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", - "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", - "dev": true, - "requires": { - "append-transform": "^1.0.0" - } - }, - "istanbul-lib-instrument": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz", - "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==", - "dev": true, - "requires": { - "@babel/core": "^7.7.5", - "@babel/parser": "^7.7.5", - "@babel/template": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", - "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "supports-color": "^6.1.0" - }, - "dependencies": { - "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", - "source-map": "^0.6.1" - }, - "dependencies": { - "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "istanbul-reports": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", - "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0" - } - }, - "jasmine": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz", - "integrity": "sha1-awicChFXax8W3xG4AUbZHU6Lij4=", - "dev": true, - "requires": { - "exit": "^0.1.2", - "glob": "^7.0.6", - "jasmine-core": "~2.8.0" - }, - "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "jasmine-core": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", - "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "jasmine-core": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.5.0.tgz", - "integrity": "sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==", - "dev": true - }, - "jasmine-spec-reporter": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz", - "integrity": "sha512-FZBoZu7VE5nR7Nilzy+Np8KuVIOxF4oXDPDknehCYBDE080EnlPu0afdZNmpGDBRCUBv3mj5qgqCRmk6W/K8vg==", - "dev": true, - "requires": { - "colors": "1.1.2" - } - }, - "jasminewd2": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz", - "integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=", - "dev": true - }, - "jest-worker": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", - "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", - "dev": true, - "requires": { - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "js-base64": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", - "integrity": "sha1-8OgK4DmkvWVLXygfyT8EqRSn/M4=" - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "dependencies": { - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - } - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.2.0.tgz", - "integrity": "sha512-2tLgY7LRNZ9Hd6gmCuBG5/OjRHQpSgJQqJoYyLLOhUgn8LdOYrjaZLcxkWnDads+AD/haWWioPNziXQcgvQJ/g==", - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "json3": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", - "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==", - "dev": true - }, - "json5": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.2.tgz", - "integrity": "sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - }, - "dependencies": { - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true, - "optional": true - } - } - }, - "jsonparse": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz", - "integrity": "sha1-MwVCrT8KZUZlt3jz6y2an6UHrGQ=" - }, - "JSONStream": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.8.4.tgz", - "integrity": "sha1-kWV9/m/4V0gwZhMrRhi2Lo9Ih70=", - "requires": { - "jsonparse": "0.0.5", - "through": ">=2.2.7 <3" - } - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "jstransform": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jstransform/-/jstransform-3.0.0.tgz", - "integrity": "sha1-olkats7o2XvzvoMNv6IxO4fNZAs=", - "requires": { - "base62": "0.1.1", - "esprima-fb": "~3001.1.0-dev-harmony-fb", - "source-map": "0.1.31" - }, - "dependencies": { - "source-map": { - "version": "0.1.31", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.31.tgz", - "integrity": "sha1-n3BNDWnZ4TioG63267T94z0VHGE=", - "requires": { - "amdefine": ">=0.0.4" - } - } - } - }, - "jszip": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.3.0.tgz", - "integrity": "sha512-EJ9k766htB1ZWnsV5ZMDkKLgA+201r/ouFF8R2OigVjVdcm2rurcBrrdXaeqBJbqnUVMko512PYmlncBKE1Huw==", - "dev": true, - "requires": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "set-immediate-shim": "~1.0.1" - } - }, - "karma": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/karma/-/karma-4.3.0.tgz", - "integrity": "sha512-NSPViHOt+RW38oJklvYxQC4BSQsv737oQlr/r06pCM+slDOr4myuI1ivkRmp+3dVpJDfZt2DmaPJ2wkx+ZZuMQ==", - "dev": true, - "requires": { - "bluebird": "^3.3.0", - "body-parser": "^1.16.1", - "braces": "^3.0.2", - "chokidar": "^3.0.0", - "colors": "^1.1.0", - "connect": "^3.6.0", - "core-js": "^3.1.3", - "di": "^0.0.1", - "dom-serialize": "^2.2.0", - "flatted": "^2.0.0", - "glob": "^7.1.1", - "graceful-fs": "^4.1.2", - "http-proxy": "^1.13.0", - "isbinaryfile": "^3.0.0", - "lodash": "^4.17.14", - "log4js": "^4.0.0", - "mime": "^2.3.1", - "minimatch": "^3.0.2", - "optimist": "^0.6.1", - "qjobs": "^1.1.4", - "range-parser": "^1.2.0", - "rimraf": "^2.6.0", - "safe-buffer": "^5.0.1", - "socket.io": "2.1.1", - "source-map": "^0.6.1", - "tmp": "0.0.33", - "useragent": "2.3.0" - }, - "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, - "mime": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "karma-chrome-launcher": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz", - "integrity": "sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg==", - "dev": true, - "requires": { - "which": "^1.2.1" - } - }, - "karma-coverage-istanbul-reporter": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-2.1.1.tgz", - "integrity": "sha512-CH8lTi8+kKXGvrhy94+EkEMldLCiUA0xMOiL31vvli9qK0T+qcXJAwWBRVJWnVWxYkTmyWar8lPz63dxX6/z1A==", - "dev": true, - "requires": { - "istanbul-api": "^2.1.6", - "minimatch": "^3.0.4" - }, - "dependencies": { - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "karma-jasmine": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-2.0.1.tgz", - "integrity": "sha512-iuC0hmr9b+SNn1DaUD2QEYtUxkS1J+bSJSn7ejdEexs7P8EYvA1CWkEdrDQ+8jVH3AgWlCNwjYsT1chjcNW9lA==", - "dev": true, - "requires": { - "jasmine-core": "^3.3" - } - }, - "karma-jasmine-html-reporter": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.5.3.tgz", - "integrity": "sha512-ci0VrjuCaFj+9d1tYlTE3KIPUCp0rz874zWWU3JgCMqGIyw5ke+BXWFPOAGAqUdCJcrMwneyvp1zFXA74MiPUA==", - "dev": true - }, - "karma-source-map-support": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", - "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", - "dev": true, - "requires": { - "source-map-support": "^0.5.5" - } - }, - "killable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", - "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", - "dev": true - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - }, - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" - }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "requires": { - "invert-kv": "^1.0.0" - } - }, - "less": { - "version": "3.11.1", - "resolved": "https://registry.npmjs.org/less/-/less-3.11.1.tgz", - "integrity": "sha512-tlWX341RECuTOvoDIvtFqXsKj072hm3+9ymRBe76/mD6O5ZZecnlAOVDlWAleF2+aohFrxNidXhv2773f6kY7g==", - "dev": true, - "requires": { - "clone": "^2.1.2", - "errno": "^0.1.1", - "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "mime": "^1.4.1", - "mkdirp": "^0.5.0", - "promise": "^7.1.1", - "request": "^2.83.0", - "source-map": "~0.6.0", - "tslib": "^1.10.0" - }, - "dependencies": { - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true, - "optional": true - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, - "optional": true - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "optional": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - } - } - }, - "less-loader": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-5.0.0.tgz", - "integrity": "sha512-bquCU89mO/yWLaUq0Clk7qCsKhsF/TZpJUzETRvJa9KSVEL9SO3ovCvdEHISBhrC81OwC8QSVX7E0bzElZj9cg==", - "dev": true, - "requires": { - "clone": "^2.1.1", - "loader-utils": "^1.1.0", - "pify": "^4.0.1" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - } - } - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "levenary": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", - "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", - "dev": true, - "requires": { - "leven": "^3.1.0" - } - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "license-webpack-plugin": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-2.1.4.tgz", - "integrity": "sha512-1Xq72fmPbTg5KofXs+yI5L4QqPFjQ6mZxoeI6D7gfiEDOtaEIk6PGrdLaej90bpDqKNHNxlQ/MW4tMAL6xMPJQ==", - "dev": true, - "requires": { - "@types/webpack-sources": "^0.1.5", - "webpack-sources": "^1.2.0" - } - }, - "lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "dev": true, - "requires": { - "immediate": "~3.0.5" - } - }, - "loader-runner": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", - "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", - "dev": true - }, - "loader-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", - "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", - "dev": true - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", - "dev": true - }, - "log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2" - } - }, - "log4js": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-4.5.1.tgz", - "integrity": "sha512-EEEgFcE9bLgaYUKuozyFfytQM2wDHtXn4tAN41pkaxpNjAykv11GVdeI4tHtmPWW4Xrgh9R/2d7XYghDVjbKKw==", - "dev": true, - "requires": { - "date-format": "^2.0.0", - "debug": "^4.1.1", - "flatted": "^2.0.0", - "rfdc": "^1.1.4", - "streamroller": "^1.0.6" - } - }, - "loglevel": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.7.tgz", - "integrity": "sha512-cY2eLFrQSAfVPhCgH1s7JI73tMbg9YC3v3+ZHVW67sBS7UxWzNEk/ZBbSfLykBWHp33dqqtOv82gjhKEi81T/A==", - "dev": true - }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "lru-cache": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", - "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" - }, - "magic-string": { - "version": "0.25.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", - "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.4" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "make-fetch-happen": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-8.0.4.tgz", - "integrity": "sha512-hIFoqGq1db0QMiy/Atr/pI1Rs4rDV+ZdGSey2SQyF3KK3u1z4aj9mS5UdNnZkdQpA+H3pGn0J3KlEwsi2x4EqA==", - "dev": true, - "requires": { - "agentkeepalive": "^4.1.0", - "cacache": "^15.0.0", - "http-cache-semantics": "^4.0.4", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^5.1.1", - "minipass": "^3.0.0", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^1.1.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "promise-retry": "^1.1.1", - "socks-proxy-agent": "^5.0.0", - "ssri": "^8.0.0" - }, - "dependencies": { - "agent-base": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.0.tgz", - "integrity": "sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==", - "dev": true, - "requires": { - "debug": "4" - } - }, - "http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, - "requires": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - } - }, - "https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "dev": true, - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "smart-buffer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", - "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==", - "dev": true - }, - "socks": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.3.tgz", - "integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==", - "dev": true, - "requires": { - "ip": "1.1.5", - "smart-buffer": "^4.1.0" - } - }, - "socks-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.0.tgz", - "integrity": "sha512-lEpa1zsWCChxiynk+lCycKuC502RxDWLKJZoIhnxrWNjLSDGYRFflHA1/228VkRcnv9TIb8w98derGbpKxJRgA==", - "dev": true, - "requires": { - "agent-base": "6", - "debug": "4", - "socks": "^2.3.3" - } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, - "makeerror": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", - "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", - "requires": { - "tmpl": "1.0.x" - } - }, - "mamacro": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", - "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==", - "dev": true - }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, - "requires": { - "p-defer": "^1.0.0" - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", - "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=" - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "requires": { - "object-visit": "^1.0.0" - } - }, - "md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "mdn-data": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", - "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", - "dev": true - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "dev": true - }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, - "memory-fs": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", - "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", - "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", - "dev": true - }, - "merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "dev": true, - "requires": { - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "methods": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/methods/-/methods-0.0.1.tgz", - "integrity": "sha1-J3yQ+L7zlwlkWoNxxRw7bGSOBow=" - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "dependencies": { - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } - } - }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - } - }, - "mime": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.5.tgz", - "integrity": "sha1-nu0HMCKov14WyFZsaGe4gyv7+hM=" - }, - "mime-db": { - "version": "1.43.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", - "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==", - "dev": true - }, - "mime-types": { - "version": "2.1.26", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", - "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", - "dev": true, - "requires": { - "mime-db": "1.43.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "mini-css-extract-plugin": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz", - "integrity": "sha512-lp3GeY7ygcgAmVIcRPBVhIkf8Us7FZjA+ILpal44qLdSu11wmjKQ3d9k15lfD7pO4esu9eUIAW7qiYIBppv40A==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "normalize-url": "1.9.1", - "schema-utils": "^1.0.0", - "webpack-sources": "^1.1.0" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - }, - "normalize-url": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", - "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", - "dev": true, - "requires": { - "object-assign": "^4.0.1", - "prepend-http": "^1.0.0", - "query-string": "^4.1.0", - "sort-keys": "^1.0.0" - } - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - } - } - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, - "minimatch": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", - "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", - "requires": { - "lru-cache": "2", - "sigmund": "~1.0.0" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "minipass": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.1.tgz", - "integrity": "sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "minipass-fetch": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.2.1.tgz", - "integrity": "sha512-ssHt0dkljEDaKmTgQ04DQgx2ag6G2gMPxA5hpcsoeTbfDgRf2fC2gNSRc6kISjD7ckCpHwwQvXxuTBK8402fXg==", - "dev": true, - "requires": { - "encoding": "^0.1.12", - "minipass": "^3.1.0", - "minipass-pipeline": "^1.2.2", - "minipass-sized": "^1.0.3", - "minizlib": "^2.0.0" - } - }, - "minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "minipass-json-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz", - "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==", - "dev": true, - "requires": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" - }, - "dependencies": { - "jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", - "dev": true - } - } - }, - "minipass-pipeline": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.2.tgz", - "integrity": "sha512-3JS5A2DKhD2g0Gg8x3yamO0pj7YeKGwVlDS90pF++kxptwx/F+B//roxf9SqYil5tQo65bijy+dAuAFZmYOouA==", - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "minizlib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.0.tgz", - "integrity": "sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA==", - "dev": true, - "requires": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - } - }, - "mississippi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", - "dev": true, - "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - } - }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "mkdirp": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", - "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=" - }, - "moment": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" - }, - "move-concurrently": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", - "dev": true, - "requires": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - } - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", - "dev": true, - "requires": { - "dns-packet": "^1.3.1", - "thunky": "^1.0.2" - } - }, - "multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", - "dev": true - }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "nan": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", - "dev": true, - "optional": true - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - } - } - }, - "natives": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.6.tgz", - "integrity": "sha512-6+TDFewD4yxY14ptjKaS63GVdtKiES1pTPyxn9Jb0rBqPMZ7VcCiooEhPNsr+mqHtMGxa/5c/HhcC4uPEUw/nA==" - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", - "dev": true - }, - "neo-async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", - "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", - "dev": true - }, - "netmask": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-1.0.6.tgz", - "integrity": "sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU=" - }, - "netrc": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/netrc/-/netrc-0.1.4.tgz", - "integrity": "sha1-a+lPysqNd63gqWcNxGCRTJRHJEQ=" - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node-forge": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz", - "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==", - "dev": true - }, - "node-libs-browser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", - "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, - "safe-buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", - "dev": true - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - } - } - } - }, - "node-releases": { - "version": "1.1.53", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.53.tgz", - "integrity": "sha512-wp8zyQVwef2hpZ/dJH7SfSrIPD6YoJz6BDQDpGEkcA0s3LpAQoxBIYmfIq6QAhC1DhwsyCgTaTTcONwX8qzCuQ==", - "dev": true - }, - "nopt": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.2.1.tgz", - "integrity": "sha1-KqCbfRdoSHs7ianFqlIzW/8Lrqc=", - "requires": { - "abbrev": "1" - } - }, - "noptify": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/noptify/-/noptify-0.0.3.tgz", - "integrity": "sha1-WPZUpz2XU98MUdlobckhBKZ/S7s=", - "requires": { - "nopt": "~2.0.0" - }, - "dependencies": { - "nopt": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.0.0.tgz", - "integrity": "sha1-ynQW8gpeP5w7hhgPlilfo9C1Lg0=", - "requires": { - "abbrev": "1" - } - } - } - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true - } - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", - "dev": true - }, - "normalize-url": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", - "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", - "dev": true - }, - "npm-bundled": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", - "dev": true, - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-install-checks": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-4.0.0.tgz", - "integrity": "sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==", - "dev": true, - "requires": { - "semver": "^7.1.1" - }, - "dependencies": { - "semver": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", - "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", - "dev": true - } - } - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", - "dev": true - }, - "npm-package-arg": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.0.1.tgz", - "integrity": "sha512-/h5Fm6a/exByzFSTm7jAyHbgOqErl9qSNJDQF32Si/ZzgwT2TERVxRxn3Jurw1wflgyVVAxnFR4fRHPM7y1ClQ==", - "dev": true, - "requires": { - "hosted-git-info": "^3.0.2", - "semver": "^7.0.0", - "validate-npm-package-name": "^3.0.0" - }, - "dependencies": { - "semver": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", - "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", - "dev": true - } - } - }, - "npm-packlist": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-2.1.1.tgz", - "integrity": "sha512-95TSDvGwujIhqfSpIiRRLodEF+y6mJMopuZdahoGzqtRDFZXGav46S0p6ngeWaiAkb5R72w6eVARhzej0HvZeQ==", - "dev": true, - "requires": { - "glob": "^7.1.6", - "ignore-walk": "^3.0.3", - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" - }, - "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "npm-pick-manifest": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.0.0.tgz", - "integrity": "sha512-PdJpXMvjqt4nftNEDpCgjBUF8yI3Q3MyuAmVB9nemnnCg32F4BPL/JFBfdj8DubgHCYUFQhtLWmBPvdsFtjWMg==", - "dev": true, - "requires": { - "npm-install-checks": "^4.0.0", - "npm-package-arg": "^8.0.0", - "semver": "^7.0.0" - }, - "dependencies": { - "semver": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", - "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", - "dev": true - } - } - }, - "npm-registry-fetch": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-8.0.0.tgz", - "integrity": "sha512-975WwLvZjX97y9UWWQ8nAyr7bw02s9xKPHqvEm5T900LQsB1HXb8Gb9ebYtCBLSX+K8gSOrO5KS/9yV/naLZmQ==", - "dev": true, - "requires": { - "@npmcli/ci-detect": "^1.0.0", - "lru-cache": "^5.1.1", - "make-fetch-happen": "^8.0.2", - "minipass": "^3.0.0", - "minipass-fetch": "^1.1.2", - "minipass-json-stream": "^1.0.1", - "minizlib": "^2.0.0", - "npm-package-arg": "^8.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "dev": true, - "requires": { - "boolbase": "~1.0.0" - } - }, - "num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", - "dev": true - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, - "object-assign": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-0.3.1.tgz", - "integrity": "sha1-Bg4qKifXwNd+x3t48Rqkf9iACNI=" - }, - "object-component": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", - "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", - "dev": true - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", - "dev": true - }, - "object-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.2.tgz", - "integrity": "sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "^3.0.0" - } - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.getownpropertydescriptors": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", - "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "object.values": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", - "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", - "has": "^1.0.3" - } - }, - "obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "open": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/open/-/open-7.0.3.tgz", - "integrity": "sha512-sP2ru2v0P290WFfv49Ap8MF6PkzGNnGlAwHweB4WR4mr5d2d0woiCluUeJ218w7/+PmoBy9JmYgD5A4mLcWOFA==", - "dev": true, - "requires": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" - } - }, - "opn": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", - "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", - "dev": true, - "requires": { - "is-wsl": "^1.1.0" - }, - "dependencies": { - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true - } - } - }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, - "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" - }, - "dependencies": { - "minimist": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", - "dev": true - } - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "requires": { - "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": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/ora/-/ora-4.0.3.tgz", - "integrity": "sha512-fnDebVFyz309A73cqCipVL1fBZewq4vwgSHfxh43vVy31mbyoQ8sCH3Oeaog/owYOs/lLlGVPCISQonTneg6Pg==", - "dev": true, - "requires": { - "chalk": "^3.0.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.2.0", - "is-interactive": "^1.0.0", - "log-symbols": "^3.0.0", - "mute-stream": "0.0.8", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "original": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", - "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", - "dev": true, - "requires": { - "url-parse": "^1.4.3" - } - }, - "os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", - "dev": true - }, - "os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "requires": { - "lcid": "^1.0.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", - "dev": true - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "p-retry": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", - "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", - "dev": true, - "requires": { - "retry": "^0.12.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "pac-proxy-agent": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-0.2.0.tgz", - "integrity": "sha1-rZApCdkvT+fMLl9Z9b9QYbz6cbI=", - "requires": { - "agent-base": "~1.0.1", - "debug": "2", - "extend": "~1.2.1", - "get-uri": "~0.1.0", - "pac-resolver": "~1.2.1", - "proxy-agent": "1", - "stream-to-array": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "extend": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-1.2.1.tgz", - "integrity": "sha1-oPX9bPyDpf5J72mNYOyKYk3UV2w=" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "pac-resolver": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-1.2.6.tgz", - "integrity": "sha1-7QOvDFtZM1Bb3T8H91F1Rm1efPs=", - "requires": { - "co": "~3.0.6", - "degenerator": "~1.0.0", - "netmask": "~1.0.4", - "regenerator": "~0.8.13", - "thunkify": "~2.1.1" - }, - "dependencies": { - "co": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/co/-/co-3.0.6.tgz", - "integrity": "sha1-FEXyJsXrlWE45oyawwFn6n0ua9o=" - } - } - }, - "pacote": { - "version": "11.1.4", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-11.1.4.tgz", - "integrity": "sha512-eUGJvSSpWFZKn3z8gig/HgnBmUl6gIWByIIaHzSyEr3tOWX0w8tFEADXtpu8HGv5E0ShCeTP6enRq8iHKCHSvw==", - "dev": true, - "requires": { - "@npmcli/git": "^2.0.1", - "@npmcli/installed-package-contents": "^1.0.5", - "@npmcli/promise-spawn": "^1.1.0", - "cacache": "^15.0.0", - "chownr": "^1.1.4", - "fs-minipass": "^2.1.0", - "infer-owner": "^1.0.4", - "lru-cache": "^5.1.1", - "minipass": "^3.0.1", - "minipass-fetch": "^1.2.1", - "mkdirp": "^1.0.3", - "npm-package-arg": "^8.0.1", - "npm-packlist": "^2.1.0", - "npm-pick-manifest": "^6.0.0", - "npm-registry-fetch": "^8.0.0", - "promise-inflight": "^1.0.1", - "promise-retry": "^1.1.1", - "read-package-json-fast": "^1.1.3", - "rimraf": "^2.7.1", - "semver": "^7.1.3", - "ssri": "^8.0.0", - "tar": "^6.0.1", - "which": "^2.0.2" - }, - "dependencies": { - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "semver": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", - "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", - "dev": true - }, - "tar": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.0.1.tgz", - "integrity": "sha512-bKhKrrz2FJJj5s7wynxy/fyxpE0CmCjmOQ1KV4KkgXFWOgoIT/NbTMnB1n+LFNrNk0SSBVGGxcK5AGsyC+pW5Q==", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, - "pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - }, - "parallel-transform": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", - "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", - "dev": true, - "requires": { - "cyclist": "^1.0.1", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" - } - }, - "parse-asn1": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", - "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", - "dev": true, - "requires": { - "asn1.js": "^4.0.0", - "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", - "optional": true - }, - "parseqs": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", - "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", - "dev": true, - "requires": { - "better-assert": "~1.0.0" - } - }, - "parseuri": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", - "dev": true, - "requires": { - "better-assert": "~1.0.0" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, - "path-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", - "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", - "dev": true - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", - "dev": true - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", - "requires": { - "through": "~2.3" - } - }, - "pbkdf2": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", - "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", - "dev": true, - "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "^2.0.0" - } - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - } - } - }, - "pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", - "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - } - }, - "portfinder": { - "version": "1.0.25", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.25.tgz", - "integrity": "sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg==", - "dev": true, - "requires": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.1" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - } - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true - }, - "postcss": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-2.2.6.tgz", - "integrity": "sha1-wENE4kSeRYa5Vfvkp093CA2EVx8=", - "requires": { - "js-base64": "~2.1.5", - "source-map": "~0.1.40" - } - }, - "postcss-calc": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.2.tgz", - "integrity": "sha512-rofZFHUg6ZIrvRwPeFktv06GdbDYLcGqh9EwiMutZg+a0oePCCw1zHOEiji6LCpyRcjTREtPASuUqeAvYlEVvQ==", - "dev": true, - "requires": { - "postcss": "^7.0.27", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.0.2" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-colormin": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", - "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "color": "^3.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-convert-values": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", - "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", - "dev": true, - "requires": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-discard-comments": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", - "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-discard-duplicates": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", - "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-discard-empty": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", - "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-discard-overridden": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", - "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-import": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-12.0.1.tgz", - "integrity": "sha512-3Gti33dmCjyKBgimqGxL3vcV8w9+bsHwO5UrBawp796+jdardbcFl4RP5w/76BwNL7aGzpKstIfF9I+kdE8pTw==", - "dev": true, - "requires": { - "postcss": "^7.0.1", - "postcss-value-parser": "^3.2.3", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-load-config": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz", - "integrity": "sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q==", - "dev": true, - "requires": { - "cosmiconfig": "^5.0.0", - "import-cwd": "^2.0.0" - } - }, - "postcss-loader": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", - "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "postcss": "^7.0.0", - "postcss-load-config": "^2.0.0", - "schema-utils": "^1.0.0" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - }, - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-merge-longhand": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", - "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", - "dev": true, - "requires": { - "css-color-names": "0.0.4", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "stylehacks": "^4.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-merge-rules": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", - "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "caniuse-api": "^3.0.0", - "cssnano-util-same-parent": "^4.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0", - "vendors": "^1.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-selector-parser": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", - "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", - "dev": true, - "requires": { - "dot-prop": "^5.2.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-minify-font-values": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", - "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", - "dev": true, - "requires": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-minify-gradients": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", - "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", - "dev": true, - "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "is-color-stop": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-minify-params": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", - "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.0", - "browserslist": "^4.0.0", - "cssnano-util-get-arguments": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "uniqs": "^2.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-minify-selectors": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", - "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-selector-parser": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", - "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", - "dev": true, - "requires": { - "dot-prop": "^5.2.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-normalize-charset": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", - "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-normalize-display-values": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", - "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", - "dev": true, - "requires": { - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-normalize-positions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", - "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", - "dev": true, - "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-normalize-repeat-style": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", - "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", - "dev": true, - "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-normalize-string": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", - "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", - "dev": true, - "requires": { - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-normalize-timing-functions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", - "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", - "dev": true, - "requires": { - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-normalize-unicode": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", - "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-normalize-url": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", - "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", - "dev": true, - "requires": { - "is-absolute-url": "^2.0.0", - "normalize-url": "^3.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-normalize-whitespace": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", - "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", - "dev": true, - "requires": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-ordered-values": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", - "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", - "dev": true, - "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-reduce-initial": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", - "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "caniuse-api": "^3.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-reduce-transforms": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", - "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", - "dev": true, - "requires": { - "cssnano-util-get-match": "^4.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-selector-parser": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz", - "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==", - "dev": true, - "requires": { - "cssesc": "^3.0.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - }, - "postcss-svgo": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz", - "integrity": "sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==", - "dev": true, - "requires": { - "is-svg": "^3.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "svgo": "^1.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-unique-selectors": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", - "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.0", - "postcss": "^7.0.0", - "uniqs": "^2.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-value-parser": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz", - "integrity": "sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg==", - "dev": true - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" - }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true - }, - "private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==" - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "dev": true, - "optional": true, - "requires": { - "asap": "~2.0.3" - } - }, - "promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", - "dev": true - }, - "promise-retry": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz", - "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=", - "dev": true, - "requires": { - "err-code": "^1.0.0", - "retry": "^0.10.0" - }, - "dependencies": { - "retry": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", - "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=", - "dev": true - } - } - }, - "protractor": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.4.3.tgz", - "integrity": "sha512-7pMAolv8Ah1yJIqaorDTzACtn3gk7BamVKPTeO5lqIGOrfosjPgXFx/z1dqSI+m5EeZc2GMJHPr5DYlodujDNA==", - "dev": true, - "requires": { - "@types/q": "^0.0.32", - "@types/selenium-webdriver": "^3.0.0", - "blocking-proxy": "^1.0.0", - "browserstack": "^1.5.1", - "chalk": "^1.1.3", - "glob": "^7.0.3", - "jasmine": "2.8.0", - "jasminewd2": "^2.1.0", - "optimist": "~0.6.0", - "q": "1.4.1", - "saucelabs": "^1.5.0", - "selenium-webdriver": "3.6.0", - "source-map-support": "~0.4.0", - "webdriver-js-extender": "2.1.0", - "webdriver-manager": "^12.0.6" - }, - "dependencies": { - "@types/q": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", - "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", - "dev": true, - "requires": { - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", - "dev": true - }, - "is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", - "dev": true, - "requires": { - "is-path-inside": "^1.0.0" - } - }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "requires": { - "path-is-inside": "^1.0.1" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "q": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", - "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "dev": true, - "requires": { - "source-map": "^0.5.6" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - }, - "webdriver-manager": { - "version": "12.1.7", - "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.7.tgz", - "integrity": "sha512-XINj6b8CYuUYC93SG3xPkxlyUc3IJbD6Vvo75CVGuG9uzsefDzWQrhz0Lq8vbPxtb4d63CZdYophF8k8Or/YiA==", - "dev": true, - "requires": { - "adm-zip": "^0.4.9", - "chalk": "^1.1.1", - "del": "^2.2.0", - "glob": "^7.0.3", - "ini": "^1.3.4", - "minimist": "^1.2.0", - "q": "^1.4.1", - "request": "^2.87.0", - "rimraf": "^2.5.2", - "semver": "^5.3.0", - "xml2js": "^0.4.17" - } - } - } - }, - "proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", - "dev": true, - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.1" - } - }, - "proxy-agent": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-1.1.1.tgz", - "integrity": "sha1-/LHu9eWJZcmV+TjwKdcp/IGFi5U=", - "requires": { - "http-proxy-agent": "0", - "https-proxy-agent": "0", - "lru-cache": "~2.5.0", - "pac-proxy-agent": "0", - "socks-proxy-agent": "1" - }, - "dependencies": { - "lru-cache": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.2.tgz", - "integrity": "sha1-H92tk4quEmPOE4aAvhs/WRwKtBw=" - } - } - }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true - }, - "public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "dev": true, - "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - }, - "dependencies": { - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" - }, - "qjobs": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", - "dev": true - }, - "qs": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.5.tgz", - "integrity": "sha1-KUsmjksNQlD23eGbO4s0k13/FO8=" - }, - "query-string": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", - "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", - "dev": true, - "requires": { - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - } - } - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true - }, - "querystringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", - "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true - }, - "raw-body": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.3.4.tgz", - "integrity": "sha1-zMfd/Ea3KGHN1btDPIQLcLbyf1Q=", - "requires": { - "bytes": "1.0.0", - "iconv-lite": "0.4.8" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.8", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.8.tgz", - "integrity": "sha1-xgGadZXyzvynAuq2lKAQvNkpjSA=" - } - } - }, - "raw-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.0.tgz", - "integrity": "sha512-iINUOYvl1cGEmfoaLjnZXt4bKfT2LJnZZib5N/LLyAphC+Dd11vNP9CNVb38j+SAJpFI1uo8j9frmih53ASy7Q==", - "dev": true, - "requires": { - "loader-utils": "^1.2.3", - "schema-utils": "^2.5.0" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - } - } - }, - "read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", - "dev": true, - "requires": { - "pify": "^2.3.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "read-package-json": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.1.tgz", - "integrity": "sha512-dAiqGtVc/q5doFz6096CcnXhpYk0ZN8dEKVkGLU0CsASt8SrgF6SF7OTKAYubfvFhWaqofl+Y8HK19GR8jwW+A==", - "dev": true, - "requires": { - "glob": "^7.1.1", - "graceful-fs": "^4.1.2", - "json-parse-better-errors": "^1.0.1", - "normalize-package-data": "^2.0.0", - "npm-normalize-package-bin": "^1.0.0" - }, - "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "read-package-json-fast": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-1.1.3.tgz", - "integrity": "sha512-MmFqiyfCXV2Dmm4jH24DEGhxdkUDFivJQj4oPZQPOKywxR7HWBE6WnMWDAapfFHi3wm1b+mhR+XHlUH0CL8axg==", - "dev": true, - "requires": { - "json-parse-even-better-errors": "^2.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "read-package-tree": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz", - "integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==", - "dev": true, - "requires": { - "read-package-json": "^2.0.0", - "readdir-scoped-modules": "^1.0.0", - "util-promisify": "^2.1.0" - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "readdir-scoped-modules": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", - "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", - "dev": true, - "requires": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" - }, - "dependencies": { - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - } - } - }, - "readdirp": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz", - "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==", - "dev": true, - "requires": { - "picomatch": "^2.0.7" - } - }, - "recast": { - "version": "0.10.33", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.10.33.tgz", - "integrity": "sha1-lCgI96oBbx+nFCxGHX5XBKqo1pc=", - "requires": { - "ast-types": "0.8.12", - "esprima-fb": "~15001.1001.0-dev-harmony-fb", - "private": "~0.1.5", - "source-map": "~0.5.0" - }, - "dependencies": { - "ast-types": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.8.12.tgz", - "integrity": "sha1-oNkOQ1G7iHcWyD/WN+v4GK9K38w=" - }, - "esprima-fb": { - "version": "15001.1001.0-dev-harmony-fb", - "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz", - "integrity": "sha1-Q761fsJujPI3092LM+QlM1d/Jlk=" - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - } - } - }, - "reduce-component": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/reduce-component/-/reduce-component-1.0.1.tgz", - "integrity": "sha1-4Mk1QsV0UhvqE98PlIjtgqt3xdo=" - }, - "reflect-metadata": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", - "dev": true - }, - "regenerate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", - "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", - "dev": true - }, - "regenerate-unicode-properties": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", - "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", - "dev": true, - "requires": { - "regenerate": "^1.4.0" - } - }, - "regenerator": { - "version": "0.8.46", - "resolved": "https://registry.npmjs.org/regenerator/-/regenerator-0.8.46.tgz", - "integrity": "sha1-FUwydoY2HtUsrWmyVF78U6PQdpY=", - "requires": { - "commoner": "~0.10.3", - "defs": "~1.1.0", - "esprima-fb": "~15001.1001.0-dev-harmony-fb", - "private": "~0.1.5", - "recast": "0.10.33", - "regenerator-runtime": "~0.9.5", - "through": "~2.3.8" - }, - "dependencies": { - "esprima-fb": { - "version": "15001.1001.0-dev-harmony-fb", - "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz", - "integrity": "sha1-Q761fsJujPI3092LM+QlM1d/Jlk=" - } - } - }, - "regenerator-runtime": { - "version": "0.9.6", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.9.6.tgz", - "integrity": "sha1-0z65XQ0gAaS+OWWXB8UbDLcc4Ck=" - }, - "regenerator-transform": { - "version": "0.14.4", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.4.tgz", - "integrity": "sha512-EaJaKPBI9GvKpvUz2mz4fhx7WPgvwRLY9v3hlNHWmAuJHI13T4nwKnNvm5RWJzEdnI5g5UwtOww+S8IdoUC2bw==", - "dev": true, - "requires": { - "@babel/runtime": "^7.8.4", - "private": "^0.1.8" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "regexp.prototype.flags": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", - "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, - "regexpu-core": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz", - "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==", - "dev": true, - "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.2.0", - "regjsgen": "^0.5.1", - "regjsparser": "^0.6.4", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.2.0" - } - }, - "regjsgen": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz", - "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==", - "dev": true - }, - "regjsparser": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", - "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - } - } - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.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" - }, - "dependencies": { - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - } - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true - }, - "resolve": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", - "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, - "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", - "dev": true, - "requires": { - "resolve-from": "^3.0.0" - } - }, - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", - "dev": true - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, - "retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", - "dev": true - }, - "rfdc": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz", - "integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==", - "dev": true - }, - "rgb-regex": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", - "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=", - "dev": true - }, - "rgba-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", - "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", - "dev": true - }, - "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "requires": { - "align-text": "^0.1.1" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "requires": { - "glob": "^7.1.3" - }, - "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "rollup": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.1.0.tgz", - "integrity": "sha512-gfE1455AEazVVTJoeQtcOq/U6GSxwoj4XPSWVsuWmgIxj7sBQNLDOSA82PbdMe+cP8ql8fR1jogPFe8Wg8g4SQ==", - "dev": true, - "requires": { - "fsevents": "~2.1.2" - } - }, - "run-async": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz", - "integrity": "sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==", - "dev": true, - "requires": { - "is-promise": "^2.1.0" - } - }, - "run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", - "dev": true, - "requires": { - "aproba": "^1.1.1" - } - }, - "rxjs": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", - "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", - "requires": { - "tslib": "^1.9.0" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sane": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/sane/-/sane-0.8.1.tgz", - "integrity": "sha1-JDpLIECBvlWTwKIrC7sRVzUS/Mc=", - "requires": { - "minimatch": "~0.2.14", - "walker": "~1.0.5", - "watch": "~0.10.0" - } - }, - "sass": { - "version": "1.26.3", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.26.3.tgz", - "integrity": "sha512-5NMHI1+YFYw4sN3yfKjpLuV9B5l7MqQ6FlkTcC4FT+oHbBRUZoSjHrrt/mE0nFXJyY2kQtU9ou9HxvFVjLFuuw==", - "dev": true, - "requires": { - "chokidar": ">=2.0.0 <4.0.0" - } - }, - "sass-loader": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-8.0.2.tgz", - "integrity": "sha512-7o4dbSK8/Ol2KflEmSco4jTjQoV988bM82P9CZdmo9hR3RLnvNc0ufMNdMrB0caq38JQ/FgF4/7RcbcfKzxoFQ==", - "dev": true, - "requires": { - "clone-deep": "^4.0.1", - "loader-utils": "^1.2.3", - "neo-async": "^2.6.1", - "schema-utils": "^2.6.1", - "semver": "^6.3.0" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "saucelabs": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz", - "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==", - "dev": true, - "requires": { - "https-proxy-agent": "^2.2.1" - }, - "dependencies": { - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "dev": true, - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - } - } - } - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, - "schema-utils": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.5.tgz", - "integrity": "sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ==", - "dev": true, - "requires": { - "ajv": "^6.12.0", - "ajv-keywords": "^3.4.1" - } - }, - "select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", - "dev": true - }, - "selenium-webdriver": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", - "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", - "dev": true, - "requires": { - "jszip": "^3.1.3", - "rimraf": "^2.5.4", - "tmp": "0.0.30", - "xml2js": "^0.4.17" - }, - "dependencies": { - "tmp": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", - "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.1" - } - } - } - }, - "selfsigned": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz", - "integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==", - "dev": true, - "requires": { - "node-forge": "0.9.0" - } - }, - "semver": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-2.3.2.tgz", - "integrity": "sha1-uYSPJdbPNjMwc+ye+IVtQvEjPlI=" - }, - "semver-dsl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz", - "integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=", - "dev": true, - "requires": { - "semver": "^5.3.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "semver-intersect": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/semver-intersect/-/semver-intersect-1.4.0.tgz", - "integrity": "sha512-d8fvGg5ycKAq0+I6nfWeCx6ffaWJCsBYU0H2Rq56+/zFePYfT8mXkB3tWBSjR5BerkHNZ5eTPIk1/LBYas35xQ==", - "dev": true, - "requires": { - "semver": "^5.0.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "serialize-javascript": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", - "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", - "dev": true - }, - "serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", - "dev": true, - "requires": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - } - } - }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "dev": true, - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", - "dev": true - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", - "dev": true - }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "requires": { - "kind-of": "^6.0.2" - }, - "dependencies": { - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - } - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "sigmund": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", - "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "simple-fmt": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/simple-fmt/-/simple-fmt-0.1.0.tgz", - "integrity": "sha1-GRv1ZqWeZTBILLJatTtKjchcOms=" - }, - "simple-is": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/simple-is/-/simple-is-0.2.0.tgz", - "integrity": "sha1-Krt1qt453rXMgVzhDmGRFkhQuvA=" - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "dev": true, - "requires": { - "is-arrayish": "^0.3.1" - }, - "dependencies": { - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "dev": true - } - } - }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true - }, - "smart-buffer": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-1.1.15.tgz", - "integrity": "sha1-fxFLW2X6s+KjWqd1uxLw0cZJvxY=" - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "requires": { - "kind-of": "^3.2.0" - } - }, - "socket.io": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz", - "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==", - "dev": true, - "requires": { - "debug": "~3.1.0", - "engine.io": "~3.2.0", - "has-binary2": "~1.0.2", - "socket.io-adapter": "~1.1.0", - "socket.io-client": "2.1.1", - "socket.io-parser": "~3.2.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "socket.io-adapter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz", - "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==", - "dev": true - }, - "socket.io-client": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz", - "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==", - "dev": true, - "requires": { - "backo2": "1.0.2", - "base64-arraybuffer": "0.1.5", - "component-bind": "1.0.0", - "component-emitter": "1.2.1", - "debug": "~3.1.0", - "engine.io-client": "~3.2.0", - "has-binary2": "~1.0.2", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "object-component": "0.0.3", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "socket.io-parser": "~3.2.0", - "to-array": "0.1.4" - }, - "dependencies": { - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "socket.io-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", - "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==", - "dev": true, - "requires": { - "component-emitter": "1.2.1", - "debug": "~3.1.0", - "isarray": "2.0.1" - }, - "dependencies": { - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "sockjs": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", - "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", - "dev": true, - "requires": { - "faye-websocket": "^0.10.0", - "uuid": "^3.0.1" - }, - "dependencies": { - "faye-websocket": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", - "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" - } - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - } - } - }, - "sockjs-client": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz", - "integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==", - "dev": true, - "requires": { - "debug": "^3.2.5", - "eventsource": "^1.0.7", - "faye-websocket": "~0.11.1", - "inherits": "^2.0.3", - "json3": "^3.3.2", - "url-parse": "^1.4.3" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "faye-websocket": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", - "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", - "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" - } - } - } - }, - "socks": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/socks/-/socks-1.1.10.tgz", - "integrity": "sha1-W4t/x8jzQcU+0FbpKbe/Tei6e1o=", - "requires": { - "ip": "^1.1.4", - "smart-buffer": "^1.0.13" - } - }, - "socks-proxy-agent": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-1.0.2.tgz", - "integrity": "sha1-Z+BrRH/lY3QX/eVzPL/f7J/+EX8=", - "requires": { - "agent-base": "~1.0.1", - "extend": "~1.2.1", - "socks": "~1.1.5" - }, - "dependencies": { - "extend": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-1.2.1.tgz", - "integrity": "sha1-oPX9bPyDpf5J72mNYOyKYk3UV2w=" - } - } - }, - "sort-keys": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", - "dev": true, - "requires": { - "is-plain-obj": "^1.0.0" - } - }, - "source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", - "dev": true - }, - "source-map": { - "version": "0.1.43", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", - "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", - "requires": { - "amdefine": ">=0.0.4" - } - }, - "source-map-loader": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-0.2.4.tgz", - "integrity": "sha512-OU6UJUty+i2JDpTItnizPrlpOIBLmQbWMuBg9q5bVtnHACqw1tn9nNwqJLbv0/00JjnJb/Ee5g5WS5vrRv7zIQ==", - "dev": true, - "requires": { - "async": "^2.5.0", - "loader-utils": "^1.1.0" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - } - } - }, - "source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "dev": true, - "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-support": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", - "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "dev": true - }, - "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", - "dev": true - }, - "spdy": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.1.tgz", - "integrity": "sha512-HeZS3PBdMA+sZSu0qwpCxl3DeALD5ASx8pAX0jZdKXSpPWbQ6SYGnlg3BBmYLx5LtiZrmkAZfErCm2oECBcioA==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - } - }, - "spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "safe-buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", - "dev": true - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - } - } - } - }, - "speed-measure-webpack-plugin": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.3.1.tgz", - "integrity": "sha512-qVIkJvbtS9j/UeZumbdfz0vg+QfG/zxonAjzefZrqzkr7xOncLVXkeGbTpzd1gjCBM4PmVNkWlkeTVhgskAGSQ==", - "dev": true, - "requires": { - "chalk": "^2.0.1" - } - }, - "split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", - "requires": { - "through": "2" - } - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "ssri": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.0.tgz", - "integrity": "sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==", - "dev": true, - "requires": { - "minipass": "^3.1.1" - } - }, - "stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "stream-browserify": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", - "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", - "dev": true, - "requires": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" - } - }, - "stream-combiner": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", - "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", - "requires": { - "duplexer": "~0.1.1" - } - }, - "stream-each": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", - "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" - } - }, - "stream-http": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", - "dev": true, - "requires": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" - } - }, - "stream-shift": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", - "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", - "dev": true - }, - "stream-to-array": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-to-array/-/stream-to-array-1.0.0.tgz", - "integrity": "sha1-lBZrsp8+ok8ILS+M0+uyzA1uyiw=" - }, - "streamroller": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-1.0.6.tgz", - "integrity": "sha512-3QC47Mhv3/aZNFpDDVO44qQb9gwB9QggMEE0sQmkTAwBVYdBRWISdsywlkfm5II1Q5y/pmrHflti/IgmIzdDBg==", - "dev": true, - "requires": { - "async": "^2.6.2", - "date-format": "^2.0.0", - "debug": "^3.2.6", - "fs-extra": "^7.0.1", - "lodash": "^4.17.14" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - } - } - }, - "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", - "dev": true - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "string.prototype.trimend": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.0.tgz", - "integrity": "sha512-EEJnGqa/xNfIg05SxiPSqRS7S9qwDhYts1TSLR1BQfYUfPe1stofgGKvwERK9+9yf+PpfBMlpBaCHucXGPQfUA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "string.prototype.trimleft": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", - "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimstart": "^1.0.0" - } - }, - "string.prototype.trimright": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", - "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimend": "^1.0.0" - } - }, - "string.prototype.trimstart": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.0.tgz", - "integrity": "sha512-iCP8g01NFYiiBOnwG1Xc3WZLyoo+RuBymwIlWncShXDDJYWN6DbnM3odslBJdgCdRlq94B5s63NWAZlcn2CS4w==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "stringmap": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stringmap/-/stringmap-0.2.2.tgz", - "integrity": "sha1-VWwTeyWPlCuHdvWy71gqoGnX0bE=" - }, - "stringset": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/stringset/-/stringset-0.2.1.tgz", - "integrity": "sha1-7yWcTjSTRDd/zRyRPdLoSMnAQrU=" - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "style-loader": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.1.3.tgz", - "integrity": "sha512-rlkH7X/22yuwFYK357fMN/BxYOorfnfq0eD7+vqlemSK4wEcejFF1dg4zxP0euBW8NrYx2WZzZ8PPFevr7D+Kw==", - "dev": true, - "requires": { - "loader-utils": "^1.2.3", - "schema-utils": "^2.6.4" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - } - } - }, - "stylehacks": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", - "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", - "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-selector-parser": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", - "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", - "dev": true, - "requires": { - "dot-prop": "^5.2.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "stylus": { - "version": "0.54.7", - "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.7.tgz", - "integrity": "sha512-Yw3WMTzVwevT6ZTrLCYNHAFmanMxdylelL3hkWNgPMeTCpMwpV3nXjpOHuBXtFv7aiO2xRuQS6OoAdgkNcSNug==", - "dev": true, - "requires": { - "css-parse": "~2.0.0", - "debug": "~3.1.0", - "glob": "^7.1.3", - "mkdirp": "~0.5.x", - "safer-buffer": "^2.1.2", - "sax": "~1.2.4", - "semver": "^6.0.0", - "source-map": "^0.7.3" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - } - } - }, - "stylus-loader": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-3.0.2.tgz", - "integrity": "sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA==", - "dev": true, - "requires": { - "loader-utils": "^1.0.2", - "lodash.clonedeep": "^4.5.0", - "when": "~3.6.x" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - } - } - }, - "superagent": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-0.17.0.tgz", - "integrity": "sha1-qtzVD75ak+cZkRGNeb8HFNYlu6g=", - "requires": { - "cookiejar": "1.3.0", - "debug": "~0.7.2", - "emitter-component": "1.0.0", - "extend": "~1.2.1", - "formidable": "1.0.14", - "methods": "0.0.1", - "mime": "1.2.5", - "qs": "0.6.5", - "reduce-component": "1.0.1" - }, - "dependencies": { - "debug": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz", - "integrity": "sha1-BuHqgILCyxTjmAbiLi9vdX+Srzk=" - }, - "extend": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-1.2.1.tgz", - "integrity": "sha1-oPX9bPyDpf5J72mNYOyKYk3UV2w=" - } - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "svgo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", - "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - } - } - }, - "symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", - "dev": true - }, - "syntax-error": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", - "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", - "requires": { - "acorn-node": "^1.2.0" - } - }, - "tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", - "dev": true - }, - "tar": { - "version": "0.1.20", - "resolved": "https://registry.npmjs.org/tar/-/tar-0.1.20.tgz", - "integrity": "sha1-QpQLrltfIsdEg2mRJvnz8nRJyxM=", - "requires": { - "block-stream": "*", - "fstream": "~0.1.28", - "inherits": "2" - } - }, - "tempfile": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-0.1.3.tgz", - "integrity": "sha1-fWtxAEcznTn4RzJ6BW2t8YMQMBA=", - "requires": { - "uuid": "~1.4.0" - } - }, - "terser": { - "version": "4.6.7", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.7.tgz", - "integrity": "sha512-fmr7M1f7DBly5cX2+rFDvmGBAaaZyPrHYK4mMdHEDAdNTqXSZgSOfqsfGq2HqPGT/1V0foZZuCZFx8CHKgAk3g==", - "dev": true, - "requires": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "terser-webpack-plugin": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.5.tgz", - "integrity": "sha512-WlWksUoq+E4+JlJ+h+U+QUzXpcsMSSNXkDy9lBVkSqDn1w23Gg29L/ary9GeJVYCGiNJJX7LnVc4bwL1N3/g1w==", - "dev": true, - "requires": { - "cacache": "^13.0.1", - "find-cache-dir": "^3.2.0", - "jest-worker": "^25.1.0", - "p-limit": "^2.2.2", - "schema-utils": "^2.6.4", - "serialize-javascript": "^2.1.2", - "source-map": "^0.6.1", - "terser": "^4.4.3", - "webpack-sources": "^1.4.3" - }, - "dependencies": { - "cacache": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz", - "integrity": "sha512-5ZvAxd05HDDU+y9BVvcqYu2LLXmPnQ0hW62h32g4xBTgL/MppR4/04NHfj/ycM2y6lmTnbw6HVi+1eN0Psba6w==", - "dev": true, - "requires": { - "chownr": "^1.1.2", - "figgy-pudding": "^3.5.1", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.2", - "infer-owner": "^1.0.4", - "lru-cache": "^5.1.1", - "minipass": "^3.0.0", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "p-map": "^3.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^2.7.1", - "ssri": "^7.0.0", - "unique-filename": "^1.1.1" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "ssri": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.0.tgz", - "integrity": "sha512-77/WrDZUWocK0mvA5NTRQyveUf+wsrIc6vyrxpS8tVvYBcX215QbafrJR3KtkpskIzoFLqqNuuYQvxaMjXJ/0g==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1", - "minipass": "^3.1.1" - } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "thunkify": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/thunkify/-/thunkify-2.1.2.tgz", - "integrity": "sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0=" - }, - "thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "dev": true - }, - "timers-browserify": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", - "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", - "dev": true, - "requires": { - "setimmediate": "^1.0.4" - } - }, - "timsort": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", - "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", - "dev": true - }, - "tiny-lr-fork": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/tiny-lr-fork/-/tiny-lr-fork-0.0.5.tgz", - "integrity": "sha1-Hpnh4qhGm3NquX2X7vqYxx927Qo=", - "requires": { - "debug": "~0.7.0", - "faye-websocket": "~0.4.3", - "noptify": "~0.0.3", - "qs": "~0.5.2" - }, - "dependencies": { - "debug": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz", - "integrity": "sha1-BuHqgILCyxTjmAbiLi9vdX+Srzk=" - }, - "qs": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz", - "integrity": "sha1-MbGtBYVnZRxSaSFQa5qHk5EaA4Q=" - } - } - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=" - }, - "to-array": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", - "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", - "dev": true - }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", - "dev": true - }, - "to-descriptor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-descriptor/-/to-descriptor-1.0.1.tgz", - "integrity": "sha1-oOZ4w068fS2uRk2DcrwhR52cK80=" - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", - "dev": true - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true - }, - "tryor": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/tryor/-/tryor-0.1.2.tgz", - "integrity": "sha1-gUXkynyv9ArN48z5Rui4u3W0Fys=" - }, - "ts-node": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz", - "integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==", - "dev": true, - "requires": { - "arg": "^4.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.6", - "yn": "^3.0.0" - } - }, - "tslib": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", - "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==" - }, - "tslint": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.18.0.tgz", - "integrity": "sha512-Q3kXkuDEijQ37nXZZLKErssQVnwCV/+23gFEMROi8IlbaBG6tXqLPQJ5Wjcyt/yHPKBC+hD5SzuGaMora+ZS6w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^3.2.0", - "glob": "^7.1.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.8.0", - "tsutils": "^2.29.0" - }, - "dependencies": { - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", - "dev": true - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", - "dev": true - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "typescript": { - "version": "3.7.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz", - "integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==", - "dev": true - }, - "ultron": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", - "dev": true - }, - "underscore.string": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", - "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=" - }, - "unglob": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/unglob/-/unglob-0.1.2.tgz", - "integrity": "sha1-8uwGKE5JGhx3YRp2CouOowAnLDg=", - "requires": { - "generator-supported": "~0.0.1", - "glob": "~3.2.8", - "minimatch": "~0.2.14" - } - }, - "unicode-canonical-property-names-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", - "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", - "dev": true - }, - "unicode-match-property-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", - "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", - "dev": true, - "requires": { - "unicode-canonical-property-names-ecmascript": "^1.0.4", - "unicode-property-aliases-ecmascript": "^1.0.4" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", - "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", - "dev": true - }, - "unicode-property-aliases-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", - "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", - "dev": true - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } - }, - "uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", - "dev": true - }, - "uniqs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", - "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", - "dev": true - }, - "unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "dev": true, - "requires": { - "unique-slug": "^2.0.0" - } - }, - "unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4" - } - }, - "universal-analytics": { - "version": "0.4.20", - "resolved": "https://registry.npmjs.org/universal-analytics/-/universal-analytics-0.4.20.tgz", - "integrity": "sha512-gE91dtMvNkjO+kWsPstHRtSwHXz0l2axqptGYp5ceg4MsuurloM0PU3pdOfpb5zBXUvyjT4PwhWK2m39uczZuw==", - "dev": true, - "requires": { - "debug": "^3.0.0", - "request": "^2.88.0", - "uuid": "^3.0.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - } - } - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "dev": true - }, - "unquote": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", - "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=", - "dev": true - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - } - } - }, - "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } - } - }, - "url-parse": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", - "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", - "dev": true, - "requires": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, - "useragent": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", - "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==", - "dev": true, - "requires": { - "lru-cache": "4.1.x", - "tmp": "0.0.x" - }, - "dependencies": { - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - } - } - }, - "util": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", - "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", - "dev": true, - "requires": { - "inherits": "2.0.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - } - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "util-promisify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/util-promisify/-/util-promisify-2.1.0.tgz", - "integrity": "sha1-PCI2R2xNMsX/PEcAKt18E7moKlM=", - "dev": true, - "requires": { - "object.getownpropertydescriptors": "^2.0.3" - } - }, - "util.promisify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", - "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.2", - "has-symbols": "^1.0.1", - "object.getownpropertydescriptors": "^2.1.0" - } - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "dev": true - }, - "uuid": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-1.4.2.tgz", - "integrity": "sha1-RTAZ9oaWam34PNxSROfJkOzDMvw=" - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "validate-npm-package-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", - "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", - "dev": true, - "requires": { - "builtins": "^1.0.3" - } - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "dev": true - }, - "vendors": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", - "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", - "dev": true - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "vm-browserify": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", - "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", - "dev": true - }, - "void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", - "dev": true - }, - "walker": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", - "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", - "requires": { - "makeerror": "1.0.x" - } - }, - "watch": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/watch/-/watch-0.10.0.tgz", - "integrity": "sha1-d3mLLaD5kQ1ZXxrOWwwiWFIfIdw=" - }, - "watchpack": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.1.tgz", - "integrity": "sha512-+IF9hfUFOrYOOaKyfaI7h7dquUIOgyEMoQMLA7OP5FxegKA2+XdXThAZ9TU2kucfhDH7rfMHs1oPYziVGWRnZA==", - "dev": true, - "requires": { - "chokidar": "^2.1.8", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" - }, - "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - } - }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "dev": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - } - }, - "fsevents": { - "version": "1.2.12", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz", - "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==", - "dev": true, - "optional": true, - "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1", - "node-pre-gyp": "*" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "bundled": true, - "extraneous": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "bundled": true, - "extraneous": true - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "bundled": true, - "extraneous": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "bundled": true, - "extraneous": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "bundled": true, - "extraneous": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "bundled": true, - "extraneous": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "bundled": true, - "extraneous": true - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "bundled": true, - "extraneous": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "bundled": true, - "extraneous": true - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "bundled": true, - "extraneous": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "bundled": true, - "extraneous": true - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "bundled": true, - "extraneous": true, - "requires": { - "ms": "^2.1.1" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "bundled": true, - "extraneous": true - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "bundled": true, - "extraneous": true - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "bundled": true, - "extraneous": true - }, - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "bundled": true, - "extraneous": true, - "requires": { - "minipass": "^2.6.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "bundled": true, - "extraneous": true - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "bundled": true, - "extraneous": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "bundled": true, - "extraneous": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "bundled": true, - "extraneous": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "bundled": true, - "extraneous": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "bundled": true, - "extraneous": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "bundled": true, - "extraneous": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "bundled": true, - "extraneous": true - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "bundled": true, - "extraneous": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "bundled": true, - "extraneous": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "bundled": true, - "extraneous": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "bundled": true, - "extraneous": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "bundled": true, - "extraneous": true - }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "bundled": true, - "extraneous": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "bundled": true, - "extraneous": true, - "requires": { - "minipass": "^2.9.0" - } - }, - "mkdirp": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", - "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", - "bundled": true, - "extraneous": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "bundled": true, - "extraneous": true - }, - "needle": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.3.3.tgz", - "integrity": "sha512-EkY0GeSq87rWp1hoq/sH/wnTWgFVhYlnIkbJ0YJFfRgEFlz2RraCjBpFQ+vrEgEdp0ThfyHADmkChEhcb7PKyw==", - "bundled": true, - "extraneous": true, - "requires": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz", - "integrity": "sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA==", - "bundled": true, - "extraneous": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4.4.2" - } - }, - "nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "bundled": true, - "extraneous": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", - "bundled": true, - "extraneous": true, - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", - "bundled": true, - "extraneous": true - }, - "npm-packlist": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", - "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", - "bundled": true, - "extraneous": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "bundled": true, - "extraneous": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "bundled": true, - "extraneous": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "bundled": true, - "extraneous": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "bundled": true, - "extraneous": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "bundled": true, - "extraneous": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "bundled": true, - "extraneous": true - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "bundled": true, - "extraneous": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "bundled": true, - "extraneous": true - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "bundled": true, - "extraneous": true - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "bundled": true, - "extraneous": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "bundled": true, - "extraneous": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "bundled": true, - "extraneous": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "bundled": true, - "extraneous": true - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "bundled": true, - "extraneous": true - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "bundled": true, - "extraneous": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bundled": true, - "extraneous": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "bundled": true, - "extraneous": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "bundled": true, - "extraneous": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "bundled": true, - "extraneous": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "bundled": true, - "extraneous": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "bundled": true, - "extraneous": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "bundled": true, - "extraneous": true - }, - "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "bundled": true, - "extraneous": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "bundled": true, - "extraneous": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "bundled": true, - "extraneous": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "bundled": true, - "extraneous": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "bundled": true, - "extraneous": true - } - } - }, - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "requires": { - "binary-extensions": "^1.0.0" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } - } - }, - "wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "dev": true, - "requires": { - "minimalistic-assert": "^1.0.0" - } - }, - "wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", - "dev": true, - "requires": { - "defaults": "^1.0.3" - } - }, - "webdriver-js-extender": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz", - "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==", - "dev": true, - "requires": { - "@types/selenium-webdriver": "^3.0.0", - "selenium-webdriver": "^3.0.1" - } - }, - "webpack": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.42.0.tgz", - "integrity": "sha512-EzJRHvwQyBiYrYqhyjW9AqM90dE4+s1/XtCfn7uWg6cS72zH+2VPFAlsnW0+W0cDi0XRjNKUMoJtpSi50+Ph6w==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-module-context": "1.8.5", - "@webassemblyjs/wasm-edit": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5", - "acorn": "^6.2.1", - "ajv": "^6.10.2", - "ajv-keywords": "^3.4.1", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^4.1.0", - "eslint-scope": "^4.0.3", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^2.4.0", - "loader-utils": "^1.2.3", - "memory-fs": "^0.4.1", - "micromatch": "^3.1.10", - "mkdirp": "^0.5.1", - "neo-async": "^2.6.1", - "node-libs-browser": "^2.2.1", - "schema-utils": "^1.0.0", - "tapable": "^1.1.3", - "terser-webpack-plugin": "^1.4.3", - "watchpack": "^1.6.0", - "webpack-sources": "^1.4.1" - }, - "dependencies": { - "acorn": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", - "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", - "dev": true - }, - "cacache": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", - "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", - "dev": true, - "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - } - }, - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1" - } - }, - "terser-webpack-plugin": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz", - "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==", - "dev": true, - "requires": { - "cacache": "^12.0.2", - "find-cache-dir": "^2.1.0", - "is-wsl": "^1.1.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^2.1.2", - "source-map": "^0.6.1", - "terser": "^4.1.2", - "webpack-sources": "^1.4.0", - "worker-farm": "^1.7.0" - } - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, - "webpack-dev-middleware": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz", - "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==", - "dev": true, - "requires": { - "memory-fs": "^0.4.1", - "mime": "^2.4.4", - "mkdirp": "^0.5.1", - "range-parser": "^1.2.1", - "webpack-log": "^2.0.0" - }, - "dependencies": { - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "mime": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", - "dev": true - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - } - } - }, - "webpack-dev-server": { - "version": "3.10.3", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.10.3.tgz", - "integrity": "sha512-e4nWev8YzEVNdOMcNzNeCN947sWJNd43E5XvsJzbAL08kGc2frm1tQ32hTJslRS+H65LCb/AaUCYU7fjHCpDeQ==", - "dev": true, - "requires": { - "ansi-html": "0.0.7", - "bonjour": "^3.5.0", - "chokidar": "^2.1.8", - "compression": "^1.7.4", - "connect-history-api-fallback": "^1.6.0", - "debug": "^4.1.1", - "del": "^4.1.1", - "express": "^4.17.1", - "html-entities": "^1.2.1", - "http-proxy-middleware": "0.19.1", - "import-local": "^2.0.0", - "internal-ip": "^4.3.0", - "ip": "^1.1.5", - "is-absolute-url": "^3.0.3", - "killable": "^1.0.1", - "loglevel": "^1.6.6", - "opn": "^5.5.0", - "p-retry": "^3.0.1", - "portfinder": "^1.0.25", - "schema-utils": "^1.0.0", - "selfsigned": "^1.10.7", - "semver": "^6.3.0", - "serve-index": "^1.9.1", - "sockjs": "0.3.19", - "sockjs-client": "1.4.0", - "spdy": "^4.0.1", - "strip-ansi": "^3.0.1", - "supports-color": "^6.1.0", - "url": "^0.11.0", - "webpack-dev-middleware": "^3.7.2", - "webpack-log": "^2.0.0", - "ws": "^6.2.1", - "yargs": "12.0.5" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - } - }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "dev": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - } - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "fsevents": { - "version": "1.2.12", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz", - "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==", - "dev": true, - "optional": true, - "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1", - "node-pre-gyp": "*" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "bundled": true, - "extraneous": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "bundled": true, - "extraneous": true - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "bundled": true, - "extraneous": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "bundled": true, - "extraneous": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "bundled": true, - "extraneous": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "bundled": true, - "extraneous": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "bundled": true, - "extraneous": true - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "bundled": true, - "extraneous": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "bundled": true, - "extraneous": true - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "bundled": true, - "extraneous": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "bundled": true, - "extraneous": true - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "bundled": true, - "extraneous": true, - "requires": { - "ms": "^2.1.1" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "bundled": true, - "extraneous": true - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "bundled": true, - "extraneous": true - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "bundled": true, - "extraneous": true - }, - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "bundled": true, - "extraneous": true, - "requires": { - "minipass": "^2.6.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "bundled": true, - "extraneous": true - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "bundled": true, - "extraneous": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "bundled": true, - "extraneous": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "bundled": true, - "extraneous": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "bundled": true, - "extraneous": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "bundled": true, - "extraneous": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "bundled": true, - "extraneous": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "bundled": true, - "extraneous": true - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "bundled": true, - "extraneous": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "bundled": true, - "extraneous": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "bundled": true, - "extraneous": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "bundled": true, - "extraneous": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "bundled": true, - "extraneous": true - }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "bundled": true, - "extraneous": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "bundled": true, - "extraneous": true, - "requires": { - "minipass": "^2.9.0" - } - }, - "mkdirp": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", - "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", - "bundled": true, - "extraneous": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "bundled": true, - "extraneous": true - }, - "needle": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.3.3.tgz", - "integrity": "sha512-EkY0GeSq87rWp1hoq/sH/wnTWgFVhYlnIkbJ0YJFfRgEFlz2RraCjBpFQ+vrEgEdp0ThfyHADmkChEhcb7PKyw==", - "bundled": true, - "extraneous": true, - "requires": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz", - "integrity": "sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA==", - "bundled": true, - "extraneous": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4.4.2" - } - }, - "nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "bundled": true, - "extraneous": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", - "bundled": true, - "extraneous": true, - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", - "bundled": true, - "extraneous": true - }, - "npm-packlist": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", - "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", - "bundled": true, - "extraneous": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "bundled": true, - "extraneous": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "bundled": true, - "extraneous": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "bundled": true, - "extraneous": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "bundled": true, - "extraneous": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "bundled": true, - "extraneous": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "bundled": true, - "extraneous": true - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "bundled": true, - "extraneous": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "bundled": true, - "extraneous": true - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "bundled": true, - "extraneous": true - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "bundled": true, - "extraneous": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "bundled": true, - "extraneous": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "bundled": true, - "extraneous": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "bundled": true, - "extraneous": true - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "bundled": true, - "extraneous": true - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "bundled": true, - "extraneous": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bundled": true, - "extraneous": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "bundled": true, - "extraneous": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "bundled": true, - "extraneous": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "bundled": true, - "extraneous": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "bundled": true, - "extraneous": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "bundled": true, - "extraneous": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "bundled": true, - "extraneous": true - }, - "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "bundled": true, - "extraneous": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "bundled": true, - "extraneous": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "bundled": true, - "extraneous": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "bundled": true, - "extraneous": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "bundled": true, - "extraneous": true - } - } - }, - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, - "is-absolute-url": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", - "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", - "dev": true - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "requires": { - "binary-extensions": "^1.0.0" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - } - }, - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - }, - "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" - } - } - } - }, - "webpack-log": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", - "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", - "dev": true, - "requires": { - "ansi-colors": "^3.0.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - } - } - }, - "webpack-merge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", - "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==", - "dev": true, - "requires": { - "lodash": "^4.17.15" - } - }, - "webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dev": true, - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "webpack-subresource-integrity": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.4.0.tgz", - "integrity": "sha512-GB1kB/LwAWC3CxwcedGhMkxGpNZxSheCe1q+KJP1bakuieAdX/rGHEcf5zsEzhKXpqsGqokgsDoD9dIkr61VDQ==", - "dev": true, - "requires": { - "webpack-sources": "^1.3.0" - } - }, - "websocket-driver": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz", - "integrity": "sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==", - "dev": true, - "requires": { - "http-parser-js": ">=0.4.0 <0.4.11", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - } - }, - "websocket-extensions": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", - "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", - "dev": true - }, - "when": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/when/-/when-3.6.4.tgz", - "integrity": "sha1-RztRfsFZ4rhQBUl6E5g/CVQS404=", - "dev": true - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "win-fork": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/win-fork/-/win-fork-1.1.1.tgz", - "integrity": "sha1-j1jgZW/KAK3IyGoriePNLWotXl4=" - }, - "window-size": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", - "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=" - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" - }, - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" - }, - "worker-farm": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", - "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", - "dev": true, - "requires": { - "errno": "~0.1.7" - } - }, - "worker-plugin": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/worker-plugin/-/worker-plugin-4.0.2.tgz", - "integrity": "sha512-V+1zSZMOOKk+uBzKyNIODLQLsx59zSIOaI75J1EMS0iR1qy+KQR3y/pQ3T0vIhvPfDFapGRMsoMvQNEL3okqSA==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - } - } - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write-to": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/write-to/-/write-to-1.1.1.tgz", - "integrity": "sha1-Xu+/aC0SjCqwwF9l3xA4c54GxqM=", - "requires": { - "dethroy": "1", - "mkdirp": "0" - } - }, - "ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } - }, - "xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", - "dev": true, - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - } - }, - "xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "dev": true - }, - "xmlhttprequest-ssl": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", - "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", - "dev": true - }, - "xregexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", - "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=" - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "yargs": { - "version": "3.27.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.27.0.tgz", - "integrity": "sha1-ISBUaTFuk5Ex1Z8toMbX+YIh6kA=", - "requires": { - "camelcase": "^1.2.1", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "os-locale": "^1.4.0", - "window-size": "^0.1.2", - "y18n": "^3.2.0" - } - }, - "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - } - } - }, - "yeast": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", - "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", - "dev": true - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - }, - "zone.js": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.10.3.tgz", - "integrity": "sha512-LXVLVEq0NNOqK/fLJo3d0kfzd4sxwn2/h67/02pjCjfKDxgx1i9QqpvtHD8CrBnSSwMw5+dy11O7FRX5mkO7Cg==" - } - } -} diff --git a/next-gen-gui/package.json b/next-gen-gui/package.json deleted file mode 100644 index e9803c3ca..000000000 --- a/next-gen-gui/package.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "name": "next-gen-gui", - "version": "1.0.0", - "scripts": { - "ng": "ng", - "start": "ng serve", - "build": "ng build", - "test": "ng test", - "lint": "ng lint", - "e2e": "ng e2e" - }, - "private": true, - "dependencies": { - "@angular/animations": "^9.1.0", - "@angular/cdk": "^9.2.0", - "@angular/common": "^9.1.0", - "@angular/compiler": "^9.1.0", - "@angular/core": "^9.1.0", - "@angular/flex-layout": "^9.0.0-beta.29", - "@angular/forms": "^9.1.0", - "@angular/material": "^9.2.0", - "@angular/platform-browser": "^9.1.0", - "@angular/platform-browser-dynamic": "^9.1.0", - "@angular/router": "^9.1.0", - "angular-in-memory-web-api": "^0.10.0", - "chart.js": "^2.9.3", - "component": "^1.1.0", - "rxjs": "^6.5.5", - "tslib": "^1.10.0", - "zone.js": "^0.10.3" - }, - "devDependencies": { - "@angular-devkit/build-angular": "^0.901.0", - "@angular/cli": "^9.1.0", - "@angular/compiler-cli": "^9.1.0", - "@angular/language-service": "^9.1.0", - "@types/jasmine": "^3.5.10", - "@types/jasminewd2": "~2.0.3", - "@types/node": "^12.12.34", - "codelyzer": "^5.2.2", - "jasmine-core": "~3.5.0", - "jasmine-spec-reporter": "~4.2.1", - "karma": "~4.3.0", - "karma-chrome-launcher": "~3.1.0", - "karma-coverage-istanbul-reporter": "~2.1.0", - "karma-jasmine": "~2.0.1", - "karma-jasmine-html-reporter": "^1.5.3", - "protractor": "~5.4.3", - "ts-node": "~8.3.0", - "tslint": "~5.18.0", - "typescript": "~3.7.5" - } -} diff --git a/next-gen-gui/screenshot.png b/next-gen-gui/screenshot.png deleted file mode 100644 index 955ccd4c7..000000000 Binary files a/next-gen-gui/screenshot.png and /dev/null differ diff --git a/next-gen-gui/src/app/api-utils.ts b/next-gen-gui/src/app/api-utils.ts deleted file mode 100644 index a75e216ea..000000000 --- a/next-gen-gui/src/app/api-utils.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { environment } from '../environments/environment' - -export const deviceID = (): String => { - // keep consistent with ShortIDStringLength in lib/protocol/deviceid.go - return environment.production ? globalThis.metadata['deviceIDShort'] : '1234567'; -} - -export const apiURL: String = '/' -export const apiRetry: number = 3; \ No newline at end of file diff --git a/next-gen-gui/src/app/app-routing.module.ts b/next-gen-gui/src/app/app-routing.module.ts deleted file mode 100644 index 06c734263..000000000 --- a/next-gen-gui/src/app/app-routing.module.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { NgModule } from '@angular/core'; -import { Routes, RouterModule } from '@angular/router'; - - -const routes: Routes = []; - -@NgModule({ - imports: [RouterModule.forRoot(routes)], - exports: [RouterModule] -}) -export class AppRoutingModule { } diff --git a/next-gen-gui/src/app/app.component.html b/next-gen-gui/src/app/app.component.html deleted file mode 100644 index d176fda39..000000000 --- a/next-gen-gui/src/app/app.component.html +++ /dev/null @@ -1,9 +0,0 @@ -
-
- - Tech UI -
- -
- - diff --git a/next-gen-gui/src/app/app.component.scss b/next-gen-gui/src/app/app.component.scss deleted file mode 100644 index f24418fbd..000000000 --- a/next-gen-gui/src/app/app.component.scss +++ /dev/null @@ -1,13 +0,0 @@ -.header { - margin: 15px 3vw 12px 3vw; - font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif; -} - -table { - width: 100%; -} - -.mat-form-field { - font-size: 14px; - width: 100%; -} \ No newline at end of file diff --git a/next-gen-gui/src/app/app.component.spec.ts b/next-gen-gui/src/app/app.component.spec.ts deleted file mode 100644 index 18d12318b..000000000 --- a/next-gen-gui/src/app/app.component.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { TestBed, async } from '@angular/core/testing'; -import { RouterTestingModule } from '@angular/router/testing'; -import { AppComponent } from './app.component'; - -describe('AppComponent', () => { - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [ - RouterTestingModule - ], - declarations: [ - AppComponent - ], - }).compileComponents(); - })); - - it('should create the app', () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/app.component.ts b/next-gen-gui/src/app/app.component.ts deleted file mode 100644 index db826164b..000000000 --- a/next-gen-gui/src/app/app.component.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Component } from '@angular/core'; -import { SystemConfigService } from './services/system-config.service'; -import { MessageService } from './services/message.service'; - -@Component({ - selector: 'app-root', - templateUrl: './app.component.html', - styleUrls: ['./app.component.scss'] -}) -export class AppComponent { - constructor( - private systemConfigService: SystemConfigService, - private messageService: MessageService, - ) { } - - restoreDefaultTheme(): void { - this.systemConfigService - .setGUITheme('default') - .subscribe(() => { - this.messageService.add('The default GUI theme has been selected. Please hit "Reload" in your browser.') - }) - } -} diff --git a/next-gen-gui/src/app/app.module.ts b/next-gen-gui/src/app/app.module.ts deleted file mode 100644 index 148b3ac2f..000000000 --- a/next-gen-gui/src/app/app.module.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { BrowserModule } from '@angular/platform-browser'; -import { NgModule } from '@angular/core'; -import { HttpClientModule, HttpClientXsrfModule } from '@angular/common/http'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; - -import { MatTableModule } from '@angular/material/table'; -import { MatPaginatorModule } from '@angular/material/paginator'; -import { MatSortModule } from '@angular/material/sort'; -import { MatInputModule } from '@angular/material/input'; -import { MatButtonToggleModule } from '@angular/material/button-toggle'; -import { MatCardModule } from '@angular/material/card'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatDialogModule } from '@angular/material/dialog'; -import { MatListModule } from '@angular/material/list' -import { MatButtonModule } from '@angular/material/button'; -import { FlexLayoutModule } from '@angular/flex-layout'; - -import { httpInterceptorProviders } from './http-interceptors'; -import { AppRoutingModule } from './app-routing.module'; -import { AppComponent } from './app.component'; - -import { StatusListComponent } from './lists/status-list/status-list.component'; -import { DeviceListComponent } from './lists/device-list/device-list.component'; -import { DonutChartComponent } from './charts/donut-chart/donut-chart.component'; -import { DashboardComponent } from './dashboard/dashboard.component'; -import { ListToggleComponent } from './list-toggle/list-toggle.component'; - -import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api'; -import { InMemoryConfigDataService } from './services/in-memory-config-data.service'; - -import { deviceID } from './api-utils'; -import { environment } from '../environments/environment'; -import { ChartItemComponent } from './charts/chart-item/chart-item.component'; -import { ChartComponent } from './charts/chart/chart.component'; -import { FolderListComponent } from './lists/folder-list/folder-list.component'; -import { DialogComponent } from './dialog/dialog.component'; -import { CardComponent, CardTitleComponent, CardContentComponent } from './card/card.component'; -import { TrimPipe } from './trim.pipe'; - -@NgModule({ - declarations: [ - AppComponent, - StatusListComponent, - DeviceListComponent, - ListToggleComponent, - DashboardComponent, - DonutChartComponent, - ChartComponent, - ChartItemComponent, - FolderListComponent, - DialogComponent, - CardComponent, - CardTitleComponent, - CardContentComponent, - TrimPipe, - ], - imports: [ - BrowserModule, - AppRoutingModule, - BrowserAnimationsModule, - MatInputModule, - MatTableModule, - MatPaginatorModule, - MatSortModule, - MatButtonToggleModule, - MatCardModule, - MatProgressBarModule, - MatDialogModule, - MatListModule, - MatButtonModule, - FlexLayoutModule, - HttpClientModule, - HttpClientXsrfModule.withOptions({ - headerName: 'X-CSRF-Token-' + deviceID(), - cookieName: 'CSRF-Token-' + deviceID(), - }), - environment.production ? - [] : HttpClientInMemoryWebApiModule.forRoot(InMemoryConfigDataService, - { dataEncapsulation: false, delay: 10 }), - ], - providers: [httpInterceptorProviders], - bootstrap: [AppComponent] -}) - -export class AppModule { } - - diff --git a/next-gen-gui/src/app/card/card.component.scss b/next-gen-gui/src/app/card/card.component.scss deleted file mode 100644 index 3e0fae75b..000000000 --- a/next-gen-gui/src/app/card/card.component.scss +++ /dev/null @@ -1,37 +0,0 @@ -// Import theming functions -@import '~@angular/material/theming'; - -@mixin tui-card-theme($theme) { - // Extract the palettes you need from the theme definition. - $primary: map-get($theme, primary); - $accent: map-get($theme, accent); - $background: map-get($theme, background); - $foreground: map-get($theme, foreground); - - .tui-card { - background-color: white; - border-radius: 4px; - height: 100%; - } - - .tui-card-title { - padding: 16px 16px 0 16px; - font-size: mat-font-size($tech-ui-typography, subheading-2); - font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif; - color: mat-color($primary); - } - - .tui-card-content { - padding: 16px; - } - - .tui-button-toggle .mat-button-toggle-appearance-standard .mat-button-toggle-label-content { - line-height: 34px; - } - - @media (prefers-color-scheme: dark) { - .tui-card { - background-color: map_get($mat-grey, 800); - } - } -} \ No newline at end of file diff --git a/next-gen-gui/src/app/card/card.component.spec.ts b/next-gen-gui/src/app/card/card.component.spec.ts deleted file mode 100644 index e141708ac..000000000 --- a/next-gen-gui/src/app/card/card.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { CardComponent } from './card.component'; - -describe('CardComponent', () => { - let component: CardComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ CardComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(CardComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/card/card.component.ts b/next-gen-gui/src/app/card/card.component.ts deleted file mode 100644 index 10a01b109..000000000 --- a/next-gen-gui/src/app/card/card.component.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { cardElevation } from '../style'; - -@Component({ - selector: 'app-card', - template: '
', - styleUrls: ['./card.component.scss'] -}) -export class CardComponent implements OnInit { - elevation: string = cardElevation; - - constructor() { } - - ngOnInit(): void { - } - -} - -@Component({ - selector: 'app-card-title', - template: '
', - styleUrls: ['./card.component.scss'] -}) -export class CardTitleComponent { - constructor() { } -} - -@Component({ - selector: 'app-card-content', - template: '
', - styleUrls: ['./card.component.scss'] -}) -export class CardContentComponent { - constructor() { } -} diff --git a/next-gen-gui/src/app/charts/chart-item/chart-item.component.html b/next-gen-gui/src/app/charts/chart-item/chart-item.component.html deleted file mode 100644 index 47af68d1e..000000000 --- a/next-gen-gui/src/app/charts/chart-item/chart-item.component.html +++ /dev/null @@ -1,4 +0,0 @@ -
- -
{{count}}
-
\ No newline at end of file diff --git a/next-gen-gui/src/app/charts/chart-item/chart-item.component.scss b/next-gen-gui/src/app/charts/chart-item/chart-item.component.scss deleted file mode 100644 index 4a7d9a572..000000000 --- a/next-gen-gui/src/app/charts/chart-item/chart-item.component.scss +++ /dev/null @@ -1,27 +0,0 @@ -@mixin chart-item-theme($theme) { - .item { - cursor: pointer; - padding: 3px 7px 3px 7px; - border-radius: 4px; - } - - .selected { - background-color: #DDDDDD; - color: #303030; - } - .selected a { - color: #303030; - text-decoration: none; - } - - @media (prefers-color-scheme: dark) { - .selected { - background-color: map_get($mat-grey, 900); - color: white; - } - - .selected a { - color: white; - } - } -} \ No newline at end of file diff --git a/next-gen-gui/src/app/charts/chart-item/chart-item.component.spec.ts b/next-gen-gui/src/app/charts/chart-item/chart-item.component.spec.ts deleted file mode 100644 index 75363d385..000000000 --- a/next-gen-gui/src/app/charts/chart-item/chart-item.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { ChartItemComponent } from './chart-item.component'; - -describe('ChartItemComponent', () => { - let component: ChartItemComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ ChartItemComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(ChartItemComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/charts/chart-item/chart-item.component.ts b/next-gen-gui/src/app/charts/chart-item/chart-item.component.ts deleted file mode 100644 index ec5ee46d5..000000000 --- a/next-gen-gui/src/app/charts/chart-item/chart-item.component.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Component, Input } from '@angular/core'; - -@Component({ - selector: 'app-chart-item', - templateUrl: './chart-item.component.html', - styleUrls: ['./chart-item.component.scss'] -}) -export class ChartItemComponent { - @Input() state: string; - @Input() count: number; - @Input('selected') - set selected(s: boolean) { - this._selected = s; - } - - _selected: boolean = true; - - constructor() { } -} diff --git a/next-gen-gui/src/app/charts/chart/chart.component.html b/next-gen-gui/src/app/charts/chart/chart.component.html deleted file mode 100644 index eefd0d62b..000000000 --- a/next-gen-gui/src/app/charts/chart/chart.component.html +++ /dev/null @@ -1,14 +0,0 @@ - - {{title | uppercase}} - -
- - -
- - -
-
-
-
\ No newline at end of file diff --git a/next-gen-gui/src/app/charts/chart/chart.component.scss b/next-gen-gui/src/app/charts/chart/chart.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/next-gen-gui/src/app/charts/chart/chart.component.spec.ts b/next-gen-gui/src/app/charts/chart/chart.component.spec.ts deleted file mode 100644 index 41d2df417..000000000 --- a/next-gen-gui/src/app/charts/chart/chart.component.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { async, TestBed } from '@angular/core/testing'; - -import { ChartComponent } from './chart.component'; -import { HttpClientModule } from '@angular/common/http'; - -class MockService { - getEach() { - // unimplemented - } -}; - -describe('ChartComponent', () => { - let component: ChartComponent; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [HttpClientModule], - providers: [ChartComponent] - }).compileComponents(); - component = TestBed.inject(ChartComponent); - })); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/charts/chart/chart.component.ts b/next-gen-gui/src/app/charts/chart/chart.component.ts deleted file mode 100644 index d18189054..000000000 --- a/next-gen-gui/src/app/charts/chart/chart.component.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { Component, OnInit, ViewChild, Input, Type } from '@angular/core'; -import Folder from '../../folder' -import { FolderService } from 'src/app/services/folder.service'; -import { DonutChartComponent } from '../donut-chart/donut-chart.component'; -import { DeviceService } from 'src/app/services/device.service'; -import Device from 'src/app/device'; -import { StType } from '../../type'; -import { FilterService } from 'src/app/services/filter.service'; -import { Observable } from 'rxjs'; - - -export interface ChartItemState { - label: string, - count: number, - color: string, - selected: boolean, -} -@Component({ - selector: 'app-chart', - templateUrl: './chart.component.html', - styleUrls: ['./chart.component.scss'] -}) - -export class ChartComponent implements OnInit { - @ViewChild(DonutChartComponent) donutChart: DonutChartComponent; - @Input() type: StType; - title: string; - chartID: string; - states: ChartItemState[] = []; - - private observer: Observable; - private activeChartState: ChartItemState; - - constructor( - private folderService: FolderService, - private deviceService: DeviceService, - private filterService: FilterService, - ) { } - - onItemSelect(s: ChartItemState) { - // Send chart item state to filter - this.filterService.changeFilter({ type: this.type, text: s.label }); - - // Deselect all other items - this.states.forEach(s => { - s.selected = false; - }); - - // Select item only - if (s !== this.activeChartState) { - s.selected = true; - this.activeChartState = s; - } else { - this.activeChartState = null; - this.filterService.changeFilter({ type: this.type, text: "" }) - } - } - - ngOnInit(): void { - switch (this.type) { - case StType.Folder: - this.title = "Folders"; - this.chartID = 'foldersChart'; - this.observer = this.folderService.folderAdded$; - break; - case StType.Device: - this.title = "Devices"; - this.chartID = 'devicesChart'; - this.observer = this.deviceService.deviceAdded$; - break; - } - } - - ngAfterViewInit() { - let totalCount: number = 0; - this.observer.subscribe( - t => { - // Count the number of folders and set chart - totalCount++; - this.donutChart.count = totalCount; - - // Get StateType and convert to string - const stateType = t.stateType; - const state = t.state; - let color; - switch (this.type) { - case StType.Folder: - color = Folder.stateTypeToColor(t.stateType); - break; - case StType.Device: - color = Device.stateTypeToColor(stateType); - break; - } - - // Check if state exists - let found: boolean = false; - this.states.forEach(s => { - if (s.label === state) { - s.count = s.count + 1; - found = true; - } - }); - - if (!found) { - this.states.push({ label: state, count: 1, color: color, selected: false }); - } - - this.donutChart.updateData(this.states); - }, - err => console.error('Observer got an error: ' + err), - () => { - } - ); - } -} \ No newline at end of file diff --git a/next-gen-gui/src/app/charts/donut-chart/donut-chart.component.html b/next-gen-gui/src/app/charts/donut-chart/donut-chart.component.html deleted file mode 100644 index 83bc1ce1b..000000000 --- a/next-gen-gui/src/app/charts/donut-chart/donut-chart.component.html +++ /dev/null @@ -1,7 +0,0 @@ -
- -
-
{{_count}}
-
{{title}}
-
-
\ No newline at end of file diff --git a/next-gen-gui/src/app/charts/donut-chart/donut-chart.component.scss b/next-gen-gui/src/app/charts/donut-chart/donut-chart.component.scss deleted file mode 100644 index 1793f03ff..000000000 --- a/next-gen-gui/src/app/charts/donut-chart/donut-chart.component.scss +++ /dev/null @@ -1,48 +0,0 @@ -.chart-container { - position: relative; - width: 100%; - height: 100%; - } - -.center { - position: absolute; - top: 0; left: 0; bottom: 0; right: 0; - width: 50%; - height: 50%; - overflow: auto; - margin: auto; -} - -.title { - font-size: calc(0.5rem + 0.625vw); - display:none; -} - -.count-total { - font-size: calc(1rem + 0.625vw); -} - -.large-count-total { - font-size: calc(0.5rem + 0.625vw); -} - -@media (max-width: 800px) { - .count-total { - font-size: calc(1.00rem + 0.625vw); - } -} -@media (min-width: 800px) and (max-width: 1000px) { - .title { - font-size: calc(0.35rem + 0.625vw); - } - - .count-total { - font-size: calc(1.35rem + 0.625vw); - } -} - -@media (min-width:1000px) { - .title { - display: inline; - } -} \ No newline at end of file diff --git a/next-gen-gui/src/app/charts/donut-chart/donut-chart.component.spec.ts b/next-gen-gui/src/app/charts/donut-chart/donut-chart.component.spec.ts deleted file mode 100644 index 340cfd687..000000000 --- a/next-gen-gui/src/app/charts/donut-chart/donut-chart.component.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { DonutChartComponent } from './donut-chart.component'; -import { HttpClientModule } from '@angular/common/http'; - -describe('DonutChartComponent', () => { - let component: DonutChartComponent; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [DonutChartComponent], - providers: [DonutChartComponent] - }).compileComponents(); - - component = TestBed.inject(DonutChartComponent); - })); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/charts/donut-chart/donut-chart.component.ts b/next-gen-gui/src/app/charts/donut-chart/donut-chart.component.ts deleted file mode 100644 index 1600af6fe..000000000 --- a/next-gen-gui/src/app/charts/donut-chart/donut-chart.component.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { Chart } from 'chart.js' -import { tooltip } from '../tooltip' -import { FilterService } from 'src/app/services/filter.service'; -import { ChartItemState } from '../chart/chart.component'; - -@Component({ - selector: 'app-donut-chart', - templateUrl: './donut-chart.component.html', - styleUrls: ['./donut-chart.component.scss'] -}) -export class DonutChartComponent { - @Input() elementID: string; - @Input() title: number; - @Output() stateEvent = new EventEmitter();; - - _count: number; - _countClass = "count-total"; - set count(n: number) { - if (n >= 1000) { // use a smaller font - this._countClass = "large-count-total" - } - this._count = n; - } - - private canvas: any; - private ctx: any; - private chart: Chart; - private states: ChartItemState[]; - - constructor(private filterService: FilterService) { } - - updateData(states: ChartItemState[]): void { - this.states = states; - // Using object destructuring - for (let i = 0; i < states.length; i++) { - let s = states[i]; - this.chart.data.labels[i] = s.label; - this.chart.data.datasets[0].data[i] = s.count; - this.chart.data.datasets[0].backgroundColor[i] = s.color; - } - this.chart.update(); - } - - removeAllData(withAnimation: boolean): void { - this.chart.data.labels.pop(); - this.chart.data.datasets.forEach((dataset) => { - dataset.data = []; - }); - this.chart.update(withAnimation); - } - - ngAfterViewInit(): void { - this.canvas = document.getElementById(this.elementID); - this.ctx = this.canvas.getContext('2d'); - this.chart = new Chart(this.ctx, { - type: 'doughnut', - data: { - datasets: [{ - data: [], - backgroundColor: [], - borderWidth: 1 - }] - }, - options: { - cutoutPercentage: 77, - responsive: true, - onClick: (e) => { - var activePoints = this.chart.getElementsAtEvent(e); - if (activePoints.length > 0) { - const index = activePoints[0]["_index"]; - this.stateEvent.emit(this.states[index]); - } - }, - legend: { - display: false - }, - tooltips: { - // Disable the on-canvas tooltip - enabled: false, - custom: tooltip(), - }, - animation: false - } - }); - } -} \ No newline at end of file diff --git a/next-gen-gui/src/app/charts/tooltip.ts b/next-gen-gui/src/app/charts/tooltip.ts deleted file mode 100644 index d46e33848..000000000 --- a/next-gen-gui/src/app/charts/tooltip.ts +++ /dev/null @@ -1,62 +0,0 @@ -// Adapted from https://www.chartjs.org/samples/latest/tooltips/custom-pie.html -export let tooltip: () => (tooltip: any) => void = - function (): (tooltip: any) => void { - return function (tooltip: any): void { - // Tooltip Element - const tooltipEl = document.getElementById('chartjs-tooltip'); - - // Hide if no tooltip - if (tooltip.opacity === 0) { - tooltipEl.style.opacity = '0'; - return; - } - - // Set caret Position - tooltipEl.classList.remove('above', 'below', 'no-transform'); - if (tooltip.yAlign) { - tooltipEl.classList.add(tooltip.yAlign); - } else { - tooltipEl.classList.add('no-transform'); - } - - function getBody(bodyItem) { - return bodyItem.lines; - } - - // Set Text - if (tooltip.body) { - let titleLines = tooltip.title || []; - const bodyLines = tooltip.body.map(getBody); - - let innerHtml = ''; - - titleLines.forEach(function (title) { - innerHtml += '' + title + ''; - }); - innerHtml += ''; - - bodyLines.forEach(function (body, i) { - let colors = tooltip.labelColors[i]; - let style = 'background:' + colors.backgroundColor; - style += '; border-color:' + colors.borderColor; - style += '; border-width: 2px'; - let span = ''; - innerHtml += '' + span + body + ''; - }); - innerHtml += ''; - - let tableRoot = tooltipEl.querySelector('table'); - tableRoot.innerHTML = innerHtml; - } - - var position = this._chart.canvas.getBoundingClientRect(); - - // Display, position, and set styles for font - tooltipEl.style.opacity = '1'; - tooltipEl.style.position = 'absolute'; - tooltipEl.style.left = position.left + window.pageXOffset + tooltip.caretX + 'px'; - tooltipEl.style.top = position.top + window.pageYOffset + tooltip.caretY + 'px'; - tooltipEl.style.padding = tooltip.yPadding + 'px ' + tooltip.xPadding + 'px'; - tooltipEl.style.pointerEvents = 'none'; - } - }; \ No newline at end of file diff --git a/next-gen-gui/src/app/completion.ts b/next-gen-gui/src/app/completion.ts deleted file mode 100644 index a52f19c5c..000000000 --- a/next-gen-gui/src/app/completion.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface Completion { - completion: number; - globalBytes: number; - needBytes: number; - needDeletes: number; - needItems: number; -} \ No newline at end of file diff --git a/next-gen-gui/src/app/connections.ts b/next-gen-gui/src/app/connections.ts deleted file mode 100644 index bf5da1a55..000000000 --- a/next-gen-gui/src/app/connections.ts +++ /dev/null @@ -1,16 +0,0 @@ -export interface SystemConnections { - connections: { deviceId?: Connection }; - total: Connection; -} - -export interface Connection { - address: string; - at: string; - clientVersion: string; - connected: boolean; - crypto: string; - inBytesTotal: number; - outBytesTotal: number; - paused: boolean; - type: string; -} \ No newline at end of file diff --git a/next-gen-gui/src/app/dashboard/dashboard.component.html b/next-gen-gui/src/app/dashboard/dashboard.component.html deleted file mode 100644 index 4e525403d..000000000 --- a/next-gen-gui/src/app/dashboard/dashboard.component.html +++ /dev/null @@ -1,16 +0,0 @@ - - - -
- - -
-
-
- - -
- -
-
\ No newline at end of file diff --git a/next-gen-gui/src/app/dashboard/dashboard.component.scss b/next-gen-gui/src/app/dashboard/dashboard.component.scss deleted file mode 100644 index 3fd8c2b3f..000000000 --- a/next-gen-gui/src/app/dashboard/dashboard.component.scss +++ /dev/null @@ -1,8 +0,0 @@ -.progress { - margin: 0 3vw 0 3vw; -} - -.grid-container { - margin: 10px calc(10px + 3.3vw); - min-width: 600px; -} \ No newline at end of file diff --git a/next-gen-gui/src/app/dashboard/dashboard.component.spec.ts b/next-gen-gui/src/app/dashboard/dashboard.component.spec.ts deleted file mode 100644 index 1ce2c37b2..000000000 --- a/next-gen-gui/src/app/dashboard/dashboard.component.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { DashboardComponent } from './dashboard.component'; -import { HttpClientModule } from '@angular/common/http'; -import { NoopAnimationsModule, BrowserAnimationsModule } from '@angular/platform-browser/animations'; - -describe('DashboardComponent', () => { - let component: DashboardComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [DashboardComponent], - imports: [ - HttpClientModule, - NoopAnimationsModule, - BrowserAnimationsModule, - ], - providers: [DashboardComponent] - }).compileComponents(); - - component = TestBed.inject(DashboardComponent); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(DashboardComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should compile', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/dashboard/dashboard.component.ts b/next-gen-gui/src/app/dashboard/dashboard.component.ts deleted file mode 100644 index 41d1fed61..000000000 --- a/next-gen-gui/src/app/dashboard/dashboard.component.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { Component, OnInit, AfterViewInit, ViewChild, AfterViewChecked } from '@angular/core'; -import { - trigger, - state, - style, - animate, - transition, -} from '@angular/animations'; -import { SystemConfigService } from '../services/system-config.service'; -import { StType } from '../type'; -import { FilterService } from '../services/filter.service'; -import { ProgressService } from '../services/progress.service'; -import { MatProgressBar } from '@angular/material/progress-bar'; -import { MessageService } from '../services/message.service'; -import { MatDialog, MatDialogRef } from '@angular/material/dialog'; -import { DialogComponent } from '../dialog/dialog.component'; -import { FolderService } from '../services/folder.service'; -import { DeviceService } from '../services/device.service'; - -@Component({ - selector: 'app-dashboard', - templateUrl: './dashboard.component.html', - styleUrls: ['./dashboard.component.scss'], - providers: [FilterService], - animations: [ - trigger('loading', [ - state('start', style({ - marginTop: '20px', - })), - state('done', style({ - marginTop: '0px', - })), - transition('start => done', [ - animate('0.2s 0.2s') - ]), - transition('done => start', [ - animate('0.2s 0.2s') - ]), - ]), - trigger('progressBar', [ - state('start', style({ - opacity: 100, - visibility: 'visible' - })), - state('done', style({ - opacity: 0, - visibility: 'hidden' - })), - transition('start => done', [ - animate('0.35s') - ]), - transition('done => start', [ - animate('0.35s') - ]), - ]), - ] -}) -export class DashboardComponent implements OnInit, AfterViewInit { - @ViewChild(MatProgressBar) progressBar: MatProgressBar; - folderChart: StType = StType.Folder; - deviceChart: StType = StType.Device; - progressValue: number = 0; - isLoading = true; - private dialogRef: MatDialogRef; - - - constructor( - private systemConfigService: SystemConfigService, - private folderService: FolderService, - private deviceService: DeviceService, - private progressService: ProgressService, - private messageService: MessageService, - public dialog: MatDialog - ) { } - - ngOnInit() { - // Request data from Rest API - this.systemConfigService.getSystemConfig().subscribe( - _ => { - // Request devices and folders for charts and lists - this.folderService.requestFolders(); - this.deviceService.requestDevices(); - } - ); - - } - - ngAfterViewInit() { - this.isLoading = true; - - // Listen for progress service changes - let t = setInterval(() => { - if (this.progressService.isComplete()) { - clearInterval(t); - this.progressValue = 100; - this.isLoading = false; - } - this.progressValue = this.progressService.percentValue; - }, 100); - - // Listen for messages from other services/components - this.messageService.messageAdded$ - .subscribe( - _ => { - // Open dialog - if (!this.dialogRef) - this.dialogRef = this.dialog.open(DialogComponent); - - this.dialogRef.afterClosed().subscribe( - _ => { - this.dialogRef = null; - this.messageService.clear(); - } - ); - }); - } -} \ No newline at end of file diff --git a/next-gen-gui/src/app/device.ts b/next-gen-gui/src/app/device.ts deleted file mode 100644 index e631add1f..000000000 --- a/next-gen-gui/src/app/device.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { colors } from './style'; -import Folder from './folder'; -import { Completion } from './completion'; - -interface Device { - deviceID: string; - name: string; - stateType: Device.StateType; - state: string; - paused: boolean; - connected: boolean; - completion: Completion; - used: boolean; // indicates if a folder is using the device - folders: Folder[]; -} - -namespace Device { - export enum StateType { - Insync = 1, - UnusedInsync, - Unknown, - Syncing, - Paused, - UnusedPaused, - Disconnected, - UnusedDisconnected, - } - - export function stateTypeToString(s: StateType): string { - switch (s) { - case StateType.Insync: - return 'Up to Date'; - case StateType.UnusedInsync: - return 'Connected (Unused)'; - case StateType.Unknown: - return 'Unknown'; - case StateType.Syncing: - return 'Syncing'; - case StateType.Paused: - return 'Paused'; - case StateType.UnusedPaused: - return 'Paused (Unused)'; - case StateType.Disconnected: - return 'Disconnected'; - case StateType.UnusedDisconnected: - return 'Disconnected (Unused)'; - } - } - - /** - * stateTypeToColor looks up a hex color string based on StateType - * @param s StateType - */ - export function stateTypeToColor(s: StateType): string { - switch (s) { - case StateType.Insync: - return colors.get("blue"); - case StateType.UnusedInsync: - return colors.get("grey"); - case StateType.Unknown: - return colors.get("grey"); - case StateType.Syncing: - return colors.get("green"); - case StateType.Paused: - return colors.get("grey"); - case StateType.UnusedPaused: - return colors.get("grey"); - case StateType.Disconnected: - return colors.get("yellow"); - case StateType.UnusedDisconnected: - return colors.get("grey"); - } - } - - export function getStateType(d: Device): StateType { - // StateType Unknown is set in DeviceService - if (d.stateType === StateType.Unknown) { - return StateType.Unknown; - } - - if (d.paused) { - return d.used ? StateType.Paused : StateType.UnusedPaused; - } - - if (d.connected) { - if (d.completion.completion === 100) { - return d.used ? StateType.Insync : StateType.UnusedInsync; - } else { - return StateType.Syncing; - } - } - - return d.used ? StateType.Disconnected : StateType.UnusedDisconnected; - } - - export function recalcCompletion(d: Device) { - if (!d || !d.completion || !d.folders) { - return; - } - var total = 0, needed = 0, deletes = 0, items = 0; - d.folders.forEach(folder => { - if (!folder || !folder.completion) - return - needed += folder.completion.needBytes; - items += folder.completion.needItems; - deletes += folder.completion.needDeletes; - }); - if (total == 0) { - d.completion.completion = 100; - d.completion.needBytes = 0; - d.completion.needItems = 0; - } else { - d.completion.completion = Math.floor(100 * (1 - needed / total)); - d.completion.needBytes = needed; - d.completion.needItems = items + deletes; - } - - if (needed == 0 && deletes > 0) { - // We don't need any data, but we have deletes that we need - // to do. Drop down the completion percentage to indicate - // that we have stuff to do. - d.completion.completion = 95; - } - } -} -export default Device; diff --git a/next-gen-gui/src/app/dialog/dialog.component.html b/next-gen-gui/src/app/dialog/dialog.component.html deleted file mode 100644 index f926daa41..000000000 --- a/next-gen-gui/src/app/dialog/dialog.component.html +++ /dev/null @@ -1,6 +0,0 @@ -
- - {{message}} - - -
\ No newline at end of file diff --git a/next-gen-gui/src/app/dialog/dialog.component.scss b/next-gen-gui/src/app/dialog/dialog.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/next-gen-gui/src/app/dialog/dialog.component.spec.ts b/next-gen-gui/src/app/dialog/dialog.component.spec.ts deleted file mode 100644 index a6bce8db3..000000000 --- a/next-gen-gui/src/app/dialog/dialog.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { DialogComponent } from './dialog.component'; - -describe('DialogComponent', () => { - let component: DialogComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ DialogComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(DialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/dialog/dialog.component.ts b/next-gen-gui/src/app/dialog/dialog.component.ts deleted file mode 100644 index 40a554339..000000000 --- a/next-gen-gui/src/app/dialog/dialog.component.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Component, OnInit, Inject } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { MessageService } from '../services/message.service'; - -export interface DialogData { - message: 'example message'; -} - -@Component({ - selector: 'app-dialog', - templateUrl: './dialog.component.html', - styleUrls: ['./dialog.component.scss'] -}) -export class DialogComponent implements OnInit { - - constructor( - public dialogRef: MatDialogRef, - public messageService: MessageService - ) { } - - ngOnInit(): void { } - - onNoClick(): void { - this.dialogRef.close(); - } -} diff --git a/next-gen-gui/src/app/folder.ts b/next-gen-gui/src/app/folder.ts deleted file mode 100644 index ca10c3486..000000000 --- a/next-gen-gui/src/app/folder.ts +++ /dev/null @@ -1,200 +0,0 @@ -import Device from './device'; -import { colors } from './style'; -import { Completion } from './completion'; - -interface Folder { - id: string; - label: string; - devices: Device[]; - status: Folder.Status; - stateType: Folder.StateType; - state: string; - paused: boolean; - completion: Completion; - path: string; -} - -namespace Folder { - export enum StateType { - Paused = 1, - Unknown, - Unshared, - WaitingToScan, - Stopped, - Scanning, - Idle, - LocalAdditions, - WaitingToSync, - PreparingToSync, - Syncing, - OutOfSync, - FailedItems, - } - - /** - * stateTypeToString returns a string representation of - * the StateType enum - * @param s StateType - */ - export function stateTypeToString(s: StateType): string { - switch (s) { - case StateType.Paused: - return 'Paused'; - case StateType.Unknown: - return 'Unknown'; - case StateType.Unshared: - return 'Unshared'; - case StateType.WaitingToSync: - return 'Waiting to Sync'; - case StateType.Stopped: - return 'Stopped'; - case StateType.Scanning: - return 'Scanning'; - case StateType.Idle: - return 'Up to Date'; - case StateType.LocalAdditions: - return 'Local Additions'; - case StateType.WaitingToScan: - return 'Waiting to Scan'; - case StateType.PreparingToSync: - return 'Preparing to Sync'; - case StateType.Syncing: - return 'Syncing'; - case StateType.OutOfSync: - return 'Out of Sync'; - case StateType.FailedItems: - return 'Failed Items'; - } - } - - /** - * stateTypeToColor looks up a hex color string based on StateType - * @param s StateType - */ - export function stateTypeToColor(s: StateType): string { - switch (s) { - case StateType.Paused: - return colors.get("grey"); - case StateType.Unknown: - return colors.get("grey"); - case StateType.Unshared: - return colors.get("grey"); - case StateType.WaitingToSync: - return colors.get("yellow"); - case StateType.Stopped: - return colors.get("grey"); - case StateType.Scanning: - return colors.get("grey"); - case StateType.Idle: - return colors.get("blue"); - case StateType.LocalAdditions: - return colors.get("grey"); - case StateType.WaitingToScan: - return colors.get("grey"); - case StateType.PreparingToSync: - return colors.get("grey"); - case StateType.Syncing: - return colors.get("green"); - case StateType.OutOfSync: - return colors.get("grey"); - case StateType.FailedItems: - return colors.get("red"); - } - } - - /** - * getStateType looks at a folder and determines the correct - * StateType to return - * - * Possible state values from API - * "idle", "scanning", "scan-waiting", "sync-waiting", "sync-preparing" - * "syncing", "error", "unknown" - * - * @param f Folder - */ - export function getStateType(f: Folder): StateType { - if (f.paused) { - return StateType.Paused; - } - - if (!f.status || (Object.keys(f.status).length === 0)) { - return StateType.Unknown; - } - - const fs: Folder.Status = f.status; - const state: string = fs.state; - - // Match API string to StateType - switch (state) { - case "idle": - return StateType.Idle; - case "scanning": - return StateType.Scanning; - case "scan-waiting": - return StateType.WaitingToScan; - case "sync-waiting": - return StateType.WaitingToSync; - case "sync-preparing": - return StateType.PreparingToSync; - case "syncing": - return StateType.Syncing; - case "error": - // legacy, the state is called "stopped" in the gui - return StateType.Stopped; - case "unknown": - return StateType.Unknown; - } - - if (fs.needTotalItems > 0) { - return StateType.OutOfSync; - } - if (fs.pullErrors > 0) { - return StateType.FailedItems; - } - if (fs.receiveOnlyTotalItems > 0) { - return StateType.LocalAdditions; - } - if (f.devices.length <= 1) { - return StateType.Unshared; - } - - return StateType.Unknown; - } - - - export interface Status { - globalBytes: number; - globalDeleted: number; - globalDirectories: number; - globalFiles: number; - globalSymlinks: number; - globalTotalItems: number; - ignorePatterns: boolean; - inSyncBytes: number; - inSyncFiles: number; - invalid: string; - localBytes: number; - localDeleted: number; - localDirectories: number; - localFiles: number; - localSymlinks: number; - needBytes: number; - needDeletes: number; - needDirectories: number; - needFiles: number; - needSymlinks: number; - needTotalItems: number; - pullErrors: number; - receiveOnlyChangedBytes: number; - receiveOnlyChangedDeletes: number; - receiveOnlyChangedDirectories: number; - receiveOnlyChangedFiles: number; - receiveOnlyChangedSymlinks: number; - receiveOnlyTotalItems: number; - sequence: number; - state: string; - stateChanged: string; - version: number; - } -} -export default Folder; \ No newline at end of file diff --git a/next-gen-gui/src/app/http-interceptors/caching.interceptor.spec.ts b/next-gen-gui/src/app/http-interceptors/caching.interceptor.spec.ts deleted file mode 100644 index ee914ec93..000000000 --- a/next-gen-gui/src/app/http-interceptors/caching.interceptor.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { CachingInterceptor } from './caching.interceptor'; - -describe('CachingInterceptor', () => { - beforeEach(() => TestBed.configureTestingModule({ - providers: [ - CachingInterceptor - ] - })); - - it('should be created', () => { - const interceptor: CachingInterceptor = TestBed.inject(CachingInterceptor); - expect(interceptor).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/http-interceptors/caching.interceptor.ts b/next-gen-gui/src/app/http-interceptors/caching.interceptor.ts deleted file mode 100644 index 7139c945d..000000000 --- a/next-gen-gui/src/app/http-interceptors/caching.interceptor.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { Injectable } from '@angular/core'; -import { - HttpRequest, - HttpHandler, - HttpEvent, - HttpInterceptor, - HttpHeaders, - HttpResponse -} from '@angular/common/http'; -import { Observable, of } from 'rxjs'; -import { tap } from 'rxjs/operators'; -import { RequestCacheService } from '../services/request-cache.service' - -@Injectable() -export class CachingInterceptor implements HttpInterceptor { - constructor(private cache: RequestCacheService) { } - - intercept(req: HttpRequest, next: HttpHandler) { - // continue if not cacheable. - if (!isCacheable(req)) { return next.handle(req); } - - const cachedResponse = this.cache.get(req); - return cachedResponse ? - of(cachedResponse) : sendRequest(req, next, this.cache); - } -} - -/** Is this request cacheable? */ -function isCacheable(req: HttpRequest) { - // Only GET requests are cacheable - return req.method === 'GET'; - /* - return req.method === 'GET' && - -1 < req.url.indexOf("url"); - */ -} - -/** - * Get server response observable by sending request to `next()`. - * Will add the response to the cache on the way out. - */ -function sendRequest( - req: HttpRequest, - next: HttpHandler, - cache: RequestCacheService): Observable> { - - // No headers allowed in npm search request - const noHeaderReq = req.clone({ headers: new HttpHeaders() }); - - return next.handle(noHeaderReq).pipe( - tap(event => { - // There may be other events besides the response. - if (event instanceof HttpResponse) { - // cache.put(req, event); // Update the cache. - } - }) - ); -} diff --git a/next-gen-gui/src/app/http-interceptors/csrf.interceptor.spec.ts b/next-gen-gui/src/app/http-interceptors/csrf.interceptor.spec.ts deleted file mode 100644 index a8f998869..000000000 --- a/next-gen-gui/src/app/http-interceptors/csrf.interceptor.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { CSRFInterceptor } from './csrf.interceptor'; - -describe('CsrfInterceptor', () => { - beforeEach(() => TestBed.configureTestingModule({ - providers: [ - CSRFInterceptor - ] - })); - - it('should be created', () => { - const interceptor: CSRFInterceptor = TestBed.inject(CSRFInterceptor); - expect(interceptor).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/http-interceptors/csrf.interceptor.ts b/next-gen-gui/src/app/http-interceptors/csrf.interceptor.ts deleted file mode 100644 index d0a4df225..000000000 --- a/next-gen-gui/src/app/http-interceptors/csrf.interceptor.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Injectable } from '@angular/core'; -import { deviceID } from '../api-utils'; -import { - HttpInterceptor, HttpHandler, HttpRequest, HttpHeaders -} from '@angular/common/http'; - -import { CookieService } from '../services/cookie.service'; - - -@Injectable() -export class CSRFInterceptor implements HttpInterceptor { - - constructor(private cookieService: CookieService) { } - - intercept(req: HttpRequest, next: HttpHandler) { - const dID: String = deviceID(); - const csrfCookie = 'CSRF-Token-' + dID - - // Clone the request and replace the original headers with - // cloned headers, updated with the CSRF information. - const csrfReq = req.clone({ - headers: req.headers.set('X-CSRF-Token-' + dID, - this.cookieService.getCookie(csrfCookie)) - }); - - // send cloned request with header to the next handler. - return next.handle(csrfReq); - } -} \ No newline at end of file diff --git a/next-gen-gui/src/app/http-interceptors/error.interceptor.spec.ts b/next-gen-gui/src/app/http-interceptors/error.interceptor.spec.ts deleted file mode 100644 index 4d083ad20..000000000 --- a/next-gen-gui/src/app/http-interceptors/error.interceptor.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { ErrorInterceptor } from './error.interceptor'; - -describe('ErrorInterceptor', () => { - beforeEach(() => TestBed.configureTestingModule({ - providers: [ - ErrorInterceptor - ] - })); - - it('should be created', () => { - const interceptor: ErrorInterceptor = TestBed.inject(ErrorInterceptor); - expect(interceptor).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/http-interceptors/error.interceptor.ts b/next-gen-gui/src/app/http-interceptors/error.interceptor.ts deleted file mode 100644 index d5dfb21ad..000000000 --- a/next-gen-gui/src/app/http-interceptors/error.interceptor.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Injectable } from '@angular/core'; -import { - HttpRequest, - HttpHandler, - HttpEvent, - HttpInterceptor, - HttpErrorResponse -} from '@angular/common/http'; -import { Observable, throwError } from 'rxjs'; -import { apiRetry } from '../api-utils'; -import { retry, catchError } from 'rxjs/operators'; -import { MessageService } from '../services/message.service'; - -@Injectable() -export class ErrorInterceptor implements HttpInterceptor { - - constructor(private messageService: MessageService) { } - - intercept(request: HttpRequest, next: HttpHandler): Observable> { - return next.handle(request) - .pipe( - retry(apiRetry), - catchError((error: HttpErrorResponse) => { - let errorMsg: string; - if (error.error instanceof ErrorEvent) { - // Client side - errorMsg = `Error: ${error.error.message}`; - } else { - // Server side - errorMsg = `Error Status: ${error.status}\nMessage: ${error.message}`; - } - console.log(errorMsg); - - this.messageService.add(errorMsg); - return throwError(errorMsg); - }) - ) - } -} diff --git a/next-gen-gui/src/app/http-interceptors/index.ts b/next-gen-gui/src/app/http-interceptors/index.ts deleted file mode 100644 index f478c61e6..000000000 --- a/next-gen-gui/src/app/http-interceptors/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* "Barrel" of Http Interceptors */ -import { HTTP_INTERCEPTORS } from '@angular/common/http'; - -import { CSRFInterceptor } from './csrf.interceptor'; -import { CachingInterceptor } from './caching.interceptor'; -import { ErrorInterceptor } from './error.interceptor'; - -/** Http interceptor providers in outside-in order */ -export const httpInterceptorProviders = [ - { provide: HTTP_INTERCEPTORS, useClass: CachingInterceptor, multi: true }, - { provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true }, - // CSRFInterceptor needs to be last - { provide: HTTP_INTERCEPTORS, useClass: CSRFInterceptor, multi: true }, -]; \ No newline at end of file diff --git a/next-gen-gui/src/app/list-toggle/list-toggle.component.html b/next-gen-gui/src/app/list-toggle/list-toggle.component.html deleted file mode 100644 index 51c58f0fb..000000000 --- a/next-gen-gui/src/app/list-toggle/list-toggle.component.html +++ /dev/null @@ -1,4 +0,0 @@ - - Folders - Devices - \ No newline at end of file diff --git a/next-gen-gui/src/app/list-toggle/list-toggle.component.scss b/next-gen-gui/src/app/list-toggle/list-toggle.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/next-gen-gui/src/app/list-toggle/list-toggle.component.spec.ts b/next-gen-gui/src/app/list-toggle/list-toggle.component.spec.ts deleted file mode 100644 index 77e0653dc..000000000 --- a/next-gen-gui/src/app/list-toggle/list-toggle.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { ListToggleComponent } from './list-toggle.component'; - -describe('ListToggleComponent', () => { - let component: ListToggleComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ListToggleComponent] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(ListToggleComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/list-toggle/list-toggle.component.ts b/next-gen-gui/src/app/list-toggle/list-toggle.component.ts deleted file mode 100644 index 733973476..000000000 --- a/next-gen-gui/src/app/list-toggle/list-toggle.component.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'; -import { StType } from '../type'; -import { MatButtonToggleGroup } from '@angular/material/button-toggle'; - - - -@Component({ - selector: 'app-list-toggle', - templateUrl: './list-toggle.component.html', - styleUrls: ['./list-toggle.component.scss'] -}) - -export class ListToggleComponent implements OnInit { - @ViewChild(MatButtonToggleGroup) group: MatButtonToggleGroup; - public listType = StType; - // public toggleValue: string = "folders"; - @Output() listTypeEvent = new EventEmitter(); - - constructor() { } - ngOnInit(): void { - } - - onSelect(t: StType): void { - this.listTypeEvent.emit(t); - } -} \ No newline at end of file diff --git a/next-gen-gui/src/app/lists/device-list/device-list.component.html b/next-gen-gui/src/app/lists/device-list/device-list.component.html deleted file mode 100644 index a27202588..000000000 --- a/next-gen-gui/src/app/lists/device-list/device-list.component.html +++ /dev/null @@ -1,31 +0,0 @@ - - Filter - - - - - - - - - - - - - - - -
{{column}} {{device[column]}} -
-
- Folders: - {{folder.label | trim}} -
-
-
- - - \ No newline at end of file diff --git a/next-gen-gui/src/app/lists/device-list/device-list.component.spec.ts b/next-gen-gui/src/app/lists/device-list/device-list.component.spec.ts deleted file mode 100644 index 7b45e3278..000000000 --- a/next-gen-gui/src/app/lists/device-list/device-list.component.spec.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { MatPaginatorModule } from '@angular/material/paginator'; -import { MatSortModule } from '@angular/material/sort'; -import { MatTableModule } from '@angular/material/table'; - -import { DeviceListComponent } from './device-list.component'; -import { HttpClientModule } from '@angular/common/http'; -import { ChangeDetectorRef } from '@angular/core'; - -describe('DeviceListComponent', () => { - let component: DeviceListComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [DeviceListComponent], - imports: [HttpClientModule], - providers: [DeviceListComponent, ChangeDetectorRef] - }).compileComponents(); - - component = TestBed.inject(DeviceListComponent); - })); - - it('should compile', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/lists/device-list/device-list.component.ts b/next-gen-gui/src/app/lists/device-list/device-list.component.ts deleted file mode 100644 index 3333b01a9..000000000 --- a/next-gen-gui/src/app/lists/device-list/device-list.component.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { AfterViewInit, Component, OnInit, ViewChild, ChangeDetectorRef, OnDestroy } from '@angular/core'; -import { MatPaginator } from '@angular/material/paginator'; -import { MatSort } from '@angular/material/sort'; -import { MatTable, MatTableDataSource } from '@angular/material/table'; - -import Device from '../../device'; -import { SystemConfigService } from '../../services/system-config.service'; -import { FilterService } from 'src/app/services/filter.service'; -import { StType } from 'src/app/type'; -import { MatInput } from '@angular/material/input'; -import { DeviceService } from 'src/app/services/device.service'; -import { trigger, state, style, transition, animate } from '@angular/animations'; - -@Component({ - selector: 'app-device-list', - templateUrl: './device-list.component.html', - styleUrls: ['../status-list/status-list.component.scss'], - animations: [ - trigger('detailExpand', [ - state('collapsed', style({ height: '0px', minHeight: '0' })), - state('expanded', style({ height: '*' })), - transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')), - ]), - ], -}) -export class DeviceListComponent implements AfterViewInit, OnInit, OnDestroy { - @ViewChild(MatPaginator) paginator: MatPaginator; - @ViewChild(MatSort) sort: MatSort; - @ViewChild(MatTable) table: MatTable; - @ViewChild(MatInput) input: MatInput; - dataSource: MatTableDataSource; - - /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ - displayedColumns = ['deviceID', 'name', 'state']; - expandedDevice: Device | null; - - constructor( - private deviceService: DeviceService, - private filterService: FilterService, - private cdr: ChangeDetectorRef, - ) { }; - - applyFilter(event: Event) { - // Set previous filter value - const filterValue = (event.target as HTMLInputElement).value; - this.filterService.previousInputs.set(StType.Device, filterValue); - this.dataSource.filter = filterValue.trim().toLowerCase(); - } - - ngOnInit() { - this.dataSource = new MatTableDataSource(); - this.dataSource.data = []; - - // Replace all data when requests are finished - this.deviceService.devicesUpdated$.subscribe( - devices => { - this.dataSource.data = devices; - } - ); - - // Add device as they come in - let devices: Device[] = []; - this.deviceService.deviceAdded$.subscribe( - device => { - devices.push(device); - this.dataSource.data = devices; - } - ); - } - - ngAfterViewInit() { - this.dataSource.sort = this.sort; - this.dataSource.paginator = this.paginator; - this.table.dataSource = this.dataSource; - - const changeText = (text: string) => { - this.dataSource.filter = text.trim().toLowerCase(); - this.input.value = text; - this.cdr.detectChanges(); - } - - // Set previous value - changeText(this.filterService.previousInputs.get(StType.Device)); - - // Listen for filter changes from other components - this.filterService.filterChanged$ - .subscribe( - input => { - if (input.type === StType.Device) { - changeText(input.text); - } - }); - } - - ngOnDestroy() { } -} \ No newline at end of file diff --git a/next-gen-gui/src/app/lists/folder-list/folder-list.component.html b/next-gen-gui/src/app/lists/folder-list/folder-list.component.html deleted file mode 100644 index 90b16c28f..000000000 --- a/next-gen-gui/src/app/lists/folder-list/folder-list.component.html +++ /dev/null @@ -1,32 +0,0 @@ - - Filter - - - - - - - - - - - - - - - - -
{{column}} {{folder[column]}} -
-
- Shared with: - {{device.name}} -
-
-
- - - \ No newline at end of file diff --git a/next-gen-gui/src/app/lists/folder-list/folder-list.component.spec.ts b/next-gen-gui/src/app/lists/folder-list/folder-list.component.spec.ts deleted file mode 100644 index 9fc88dcd3..000000000 --- a/next-gen-gui/src/app/lists/folder-list/folder-list.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { FolderListComponent } from './folder-list.component'; -import { HttpClientModule } from '@angular/common/http'; -import { ChangeDetectorRef } from '@angular/core'; - -describe('FolderListComponent', () => { - let component: FolderListComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [FolderListComponent], - imports: [HttpClientModule], - providers: [FolderListComponent, ChangeDetectorRef] - }) - .compileComponents(); - - component = TestBed.inject(FolderListComponent); - })); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/lists/folder-list/folder-list.component.ts b/next-gen-gui/src/app/lists/folder-list/folder-list.component.ts deleted file mode 100644 index 357108281..000000000 --- a/next-gen-gui/src/app/lists/folder-list/folder-list.component.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { AfterViewInit, Component, OnInit, ViewChild, ChangeDetectorRef, OnDestroy } from '@angular/core'; -import { MatPaginator } from '@angular/material/paginator'; -import { MatSort } from '@angular/material/sort'; -import { MatTable, MatTableDataSource } from '@angular/material/table'; - -import Folder from '../../folder'; -import { SystemConfigService } from '../../services/system-config.service'; -import { FilterService } from 'src/app/services/filter.service'; -import { StType } from 'src/app/type'; -import { MatInput } from '@angular/material/input'; -import { FolderService } from 'src/app/services/folder.service'; -import { trigger, state, style, transition, animate } from '@angular/animations'; - -@Component({ - selector: 'app-folder-list', - templateUrl: './folder-list.component.html', - styleUrls: ['../status-list/status-list.component.scss'], - animations: [ - trigger('detailExpand', [ - state('collapsed', style({ height: '0px', minHeight: '0' })), - state('expanded', style({ height: '*' })), - transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')), - ]), - ], -}) -export class FolderListComponent implements AfterViewInit, OnInit, OnDestroy { - @ViewChild(MatPaginator) paginator: MatPaginator; - @ViewChild(MatSort) sort: MatSort; - @ViewChild(MatTable) table: MatTable; - @ViewChild(MatInput) input: MatInput; - dataSource: MatTableDataSource; - - /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ - displayedColumns = [ - "id", - "label", - "path", - "state" - ]; - - expandedFolder: Folder | null; - - constructor( - private folderService: FolderService, - private filterService: FilterService, - private cdr: ChangeDetectorRef, - ) { - }; - - applyFilter(event: Event) { - const filterValue = (event.target as HTMLInputElement).value; - this.filterService.previousInputs.set(StType.Folder, filterValue); - this.dataSource.filter = filterValue.trim().toLowerCase(); - } - - ngOnInit() { - this.dataSource = new MatTableDataSource(); - this.dataSource.data = []; - - // Replace all data when requests are finished - this.folderService.foldersUpdated$.subscribe( - folders => { - this.dataSource.data = folders; - } - ); - - // Add device as they come in - let folders: Folder[] = []; - this.folderService.folderAdded$.subscribe( - folder => { - folders.push(folder); - this.dataSource.data = folders; - } - );; - } - - ngAfterViewInit() { - this.dataSource.sort = this.sort; - this.dataSource.paginator = this.paginator; - this.table.dataSource = this.dataSource; - - const changeText = (text: string) => { - this.dataSource.filter = text.trim().toLowerCase(); - this.input.value = text; - this.cdr.detectChanges(); - } - - // Set previous value - changeText(this.filterService.previousInputs.get(StType.Folder)); - - // Listen for filter changes from other components - this.filterService.filterChanged$ - .subscribe( - input => { - if (input.type === StType.Folder) { - changeText(input.text); - } - }); - } - - ngOnDestroy() { } -} \ No newline at end of file diff --git a/next-gen-gui/src/app/lists/status-list/status-list.component.html b/next-gen-gui/src/app/lists/status-list/status-list.component.html deleted file mode 100644 index bf2b5999d..000000000 --- a/next-gen-gui/src/app/lists/status-list/status-list.component.html +++ /dev/null @@ -1,10 +0,0 @@ - -
- {{title | uppercase}} - -
- - - - -
\ No newline at end of file diff --git a/next-gen-gui/src/app/lists/status-list/status-list.component.scss b/next-gen-gui/src/app/lists/status-list/status-list.component.scss deleted file mode 100644 index 61d04678d..000000000 --- a/next-gen-gui/src/app/lists/status-list/status-list.component.scss +++ /dev/null @@ -1,70 +0,0 @@ -.status-list .tui-card-toggle { - padding: 16px 16px 0 16px; -} - -.full-width-table { - width: 100%; -} - -.mat-form-field { - font-size: 14px; - width: 100%; -} - -tr.detail-row { - height: 0; -} - -tr.table-row:not(.expanded-row):hover { - background: whitesmoke; - color: #303030; -} - -tr.table-row:not(.expanded-row):active { - background: #DDDDDD; - color: #303030; -} - -.expanded-row { - background: #DDDDDD; - color: #303030; -} - -.table-row td { - border-bottom-width: 0; -} - -.table-detail { - overflow: hidden; - display: flex; -} - -.detail-items { - padding: 16px; -} - -// Hide empty name -.item-name:empty { - display: none; -} - -.item-name:not(:last-child):after { - content: ", "; -} - -@media (prefers-color-scheme: dark) { - tr.table-row:not(.expanded-row):hover { - background: #212121; - color: white; - } - - tr.table-row:not(.expanded-row):active { - background: #212121; - color: white; - } - - .expanded-row { - background: #212121; - color: white; - } -} \ No newline at end of file diff --git a/next-gen-gui/src/app/lists/status-list/status-list.component.spec.ts b/next-gen-gui/src/app/lists/status-list/status-list.component.spec.ts deleted file mode 100644 index c4daf34c4..000000000 --- a/next-gen-gui/src/app/lists/status-list/status-list.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { StatusListComponent } from './status-list.component'; - -describe('StatusListComponent', () => { - let component: StatusListComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [StatusListComponent] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(StatusListComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/lists/status-list/status-list.component.ts b/next-gen-gui/src/app/lists/status-list/status-list.component.ts deleted file mode 100644 index 2ab3c181d..000000000 --- a/next-gen-gui/src/app/lists/status-list/status-list.component.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Component, ViewChild, AfterViewInit, ChangeDetectorRef } from '@angular/core'; -import { StType } from '../../type'; -import { cardElevation } from '../../style'; -import { FilterService } from 'src/app/services/filter.service'; -import { ListToggleComponent } from 'src/app/list-toggle/list-toggle.component'; - - -@Component({ - selector: 'app-status-list', - templateUrl: './status-list.component.html', - styleUrls: ['./status-list.component.scss'] -}) -export class StatusListComponent { - @ViewChild(ListToggleComponent) toggle: ListToggleComponent; - currentListType: StType = StType.Folder; - listType = StType; // used in html - elevation: string = cardElevation; - title: string = 'Status'; - - constructor( - private filterService: FilterService, - private cdr: ChangeDetectorRef, - ) { } - - ngAfterViewInit() { - // Listen for filter changes from other components - this.filterService.filterChanged$.subscribe( - input => { - this.currentListType = input.type; - - switch (input.type) { - case StType.Folder: - this.toggle.group.value = "folders"; - break; - case StType.Device: - this.toggle.group.value = "devices"; - break; - } - }); - - this.cdr.detectChanges(); // manually detect changes - } - - onToggle(t: StType) { - this.currentListType = t; - } -} diff --git a/next-gen-gui/src/app/mocks/mock-db-completion.ts b/next-gen-gui/src/app/mocks/mock-db-completion.ts deleted file mode 100644 index a0fd65480..000000000 --- a/next-gen-gui/src/app/mocks/mock-db-completion.ts +++ /dev/null @@ -1,26 +0,0 @@ -export const dbCompletion = - [ - { - "device": "YZJBJFX-RDBL7WY-6ZGKJ2D-4MJB4E7-ZATSDUY-LD6Y3L3-MLFUYWE-AEMXJAC", - "completion": 100, - "globalBytes": 156793013575, - "needBytes": 0, - "needDeletes": 0, - "needItems": 0 - }, - { - "completion": 80, - "globalBytes": 3013575, - "needBytes": 100, - "needDeletes": 0, - "needItems": 0 - } - ] -/* -{ - "completion": 100, - "globalBytes": 156793013575, - "needBytes": 0, - "needDeletes": 0 -} -*/ \ No newline at end of file diff --git a/next-gen-gui/src/app/mocks/mock-db-status.ts b/next-gen-gui/src/app/mocks/mock-db-status.ts deleted file mode 100644 index 03c5a51c6..000000000 --- a/next-gen-gui/src/app/mocks/mock-db-status.ts +++ /dev/null @@ -1,46 +0,0 @@ -export const dbStatus = - [ - { "folder": "GXWxf-3zgnU", "state": "active" }, - { "folder": "Tyeho-ncvqp", "state": "idle" }, - { "folder": "Ihpqp-3zgnq", "state": "idle" }, - { "folder": "Abqqp-3zgnU", "state": "idle" }, - { "folder": "Bawer-3zgnU", "state": "idle" }, - { "folder": "Zpohq-3zgnU", "state": "idle" }, - { "folder": "Lkmbn-3zgnU", "state": "idle" }, - { "folder": "Poqff-3zgnU", "state": "idle" } - ] -/*[{ - "folder": "GXWxf-3zgnU", - "globalBytes": 0, - "globalDeleted": 0, - "globalDirectories": 0, - "globalFiles": 0, - "globalSymlinks": 0, - "globalTotalItems": 0, - "ignorePatterns": false, - "inSyncBytes": 0, - "inSyncFiles": 0, - "invalid": "", - "localBytes": 0, - "localDeleted": 0, - "localDirectories": 0, - "localFiles": 0, - "localSymlinks": 0, - "localTotalItems": 0, - "needBytes": 0, - "needDeletes": 0, - "needDirectories": 0, - "needFiles": 0, - "needSymlinks": 0, - "needTotalItems": 0, - "pullErrors": 0, - "receiveOnlyChangedBytes": 0, - "receiveOnlyChangedDeletes": 0, - "receiveOnlyChangedDirectories": 0, - "receiveOnlyChangedFiles": 0, - "receiveOnlyChangedSymlinks": 0, - "sequence": 0, - "state": "idle", - "stateChanged": "2018-08-08T07:04:57.301064781+02:00", - "version": 0 -}]*/; \ No newline at end of file diff --git a/next-gen-gui/src/app/mocks/mock-system-config.ts b/next-gen-gui/src/app/mocks/mock-system-config.ts deleted file mode 100644 index b5cbe80a0..000000000 --- a/next-gen-gui/src/app/mocks/mock-system-config.ts +++ /dev/null @@ -1,67 +0,0 @@ -export const config = { - "version": 15, - "folders": [ - { "id": "GXWxf-3zgnU", "label": "MyFolder", "path": "...", "type": "sendreceive", "devices": [{ "deviceID": "YZJBJFX-RDBL7WY-6ZGKJ2D-4MJB4E7-ZATSDUY-LD6Y3L3-MLFUYWE-AEMXJAC" }], "rescanIntervalS": 60, "ignorePerms": false, "autoNormalize": true, "minDiskFreePct": 1, "versioning": { "type": "simple", "params": { "keep": "5" } }, "copiers": 0, "pullers": 0, "hashers": 0, "order": "random", "ignoreDelete": false, "scanProgressIntervalS": 0, "pullerSleepS": 0, "pullerPauseS": 0, "maxConflicts": 10, "disableSparseFiles": false, "disableTempIndexes": false, "fsync": false, "invalid": "" }, - { "id": "Tyeho-ncvqp", "label": "MyFolder", "path": "...", "type": "sendreceive", "devices": [{ "deviceID": "..." }], "rescanIntervalS": 60, "ignorePerms": false, "autoNormalize": true, "minDiskFreePct": 1, "versioning": { "type": "simple", "params": { "keep": "5" } }, "copiers": 0, "pullers": 0, "hashers": 0, "order": "random", "ignoreDelete": false, "scanProgressIntervalS": 0, "pullerSleepS": 0, "pullerPauseS": 0, "maxConflicts": 10, "disableSparseFiles": false, "disableTempIndexes": false, "fsync": false, "invalid": "" }, - { "id": "Ihpqp-3zgnq", "label": "MyFolder", "path": "...", "type": "sendreceive", "devices": [{ "deviceID": "..." }], "rescanIntervalS": 60, "ignorePerms": false, "autoNormalize": true, "minDiskFreePct": 1, "versioning": { "type": "simple", "params": { "keep": "5" } }, "copiers": 0, "pullers": 0, "hashers": 0, "order": "random", "ignoreDelete": false, "scanProgressIntervalS": 0, "pullerSleepS": 0, "pullerPauseS": 0, "maxConflicts": 10, "disableSparseFiles": false, "disableTempIndexes": false, "fsync": false, "invalid": "" }, - { "id": "Abqqp-3zgnU", "label": "MyFolder", "path": "...", "type": "sendreceive", "devices": [{ "deviceID": "..." }], "rescanIntervalS": 60, "ignorePerms": false, "autoNormalize": true, "minDiskFreePct": 1, "versioning": { "type": "simple", "params": { "keep": "5" } }, "copiers": 0, "pullers": 0, "hashers": 0, "order": "random", "ignoreDelete": false, "scanProgressIntervalS": 0, "pullerSleepS": 0, "pullerPauseS": 0, "maxConflicts": 10, "disableSparseFiles": false, "disableTempIndexes": false, "fsync": false, "invalid": "" }, - { "id": "Bawer-3zgnU", "label": "MyFolder", "path": "...", "type": "sendreceive", "devices": [{ "deviceID": "..." }], "rescanIntervalS": 60, "ignorePerms": false, "autoNormalize": true, "minDiskFreePct": 1, "versioning": { "type": "simple", "params": { "keep": "5" } }, "copiers": 0, "pullers": 0, "hashers": 0, "order": "random", "ignoreDelete": false, "scanProgressIntervalS": 0, "pullerSleepS": 0, "pullerPauseS": 0, "maxConflicts": 10, "disableSparseFiles": false, "disableTempIndexes": false, "fsync": false, "invalid": "" }, - { "id": "Zpohq-3zgnU", "label": "MyFolder", "path": "...", "type": "sendreceive", "devices": [{ "deviceID": "..." }], "rescanIntervalS": 60, "ignorePerms": false, "autoNormalize": true, "minDiskFreePct": 1, "versioning": { "type": "simple", "params": { "keep": "5" } }, "copiers": 0, "pullers": 0, "hashers": 0, "order": "random", "ignoreDelete": false, "scanProgressIntervalS": 0, "pullerSleepS": 0, "pullerPauseS": 0, "maxConflicts": 10, "disableSparseFiles": false, "disableTempIndexes": false, "fsync": false, "invalid": "" }, - { "id": "Lkmbn-3zgnU", "label": "MyFolder", "path": "...", "type": "sendreceive", "devices": [{ "deviceID": "..." }], "rescanIntervalS": 60, "ignorePerms": false, "autoNormalize": true, "minDiskFreePct": 1, "versioning": { "type": "simple", "params": { "keep": "5" } }, "copiers": 0, "pullers": 0, "hashers": 0, "order": "random", "ignoreDelete": false, "scanProgressIntervalS": 0, "pullerSleepS": 0, "pullerPauseS": 0, "maxConflicts": 10, "disableSparseFiles": false, "disableTempIndexes": false, "fsync": false, "invalid": "" }, - { "id": "Poqff-3zgnU", "label": "MyFolder", "path": "...", "type": "sendreceive", "devices": [{ "deviceID": "..." }], "rescanIntervalS": 60, "ignorePerms": false, "autoNormalize": true, "minDiskFreePct": 1, "versioning": { "type": "simple", "params": { "keep": "5" } }, "copiers": 0, "pullers": 0, "hashers": 0, "order": "random", "ignoreDelete": false, "scanProgressIntervalS": 0, "pullerSleepS": 0, "pullerPauseS": 0, "maxConflicts": 10, "disableSparseFiles": false, "disableTempIndexes": false, "fsync": false, "invalid": "" }, - ], - "devices": [ - { "deviceID": "YZJBJFX-RDBL7WY-6ZGKJ2D-4MJB4E7-ZATSDUY-LD6Y3L3-MLFUYWE-AEMXJAC", "name": "Laptop", "addresses": ["dynamic", "tcp://192.168.1.2:22000"], "compression": "metadata", "certName": "", "introducer": false }, - { "deviceID": "...", "name": "Server", "addresses": ["dynamic", "tcp://192.168.1.3:22000"], "compression": "metadata", "certName": "", "introducer": false }, - ], - "gui": { - "enabled": true, - "address": "127.0.0.1:8384", - "user": "Username", - "password": "$2a$10$ZFws69T4FlvWwsqeIwL.TOo5zOYqsa/.TxlUnsGYS.j3JvjFTmxo6", - "useTLS": false, - "apiKey": "pGahcht56664QU5eoFQW6szbEG6Ec2Cr", - "insecureAdminAccess": false, - "theme": "default" - }, - "options": { - "listenAddresses": [ - "default" - ], - "globalAnnounceServers": [ - "default" - ], - "globalAnnounceEnabled": true, - "localAnnounceEnabled": true, - "localAnnouncePort": 21027, - "localAnnounceMCAddr": "[ff12::8384]:21027", - "maxSendKbps": 0, - "maxRecvKbps": 0, - "reconnectionIntervalS": 60, - "relaysEnabled": true, - "relayReconnectIntervalM": 10, - "startBrowser": false, - "natEnabled": true, - "natLeaseMinutes": 60, - "natRenewalMinutes": 30, - "natTimeoutSeconds": 10, - "urAccepted": -1, - "urUniqueId": "", - "urURL": "https://data.syncthing.net/newdata", - "urPostInsecurely": false, - "urInitialDelayS": 1800, - "restartOnWakeup": true, - "autoUpgradeIntervalH": 12, - "keepTemporariesH": 24, - "cacheIgnoredFiles": false, - "progressUpdateIntervalS": 5, - "limitBandwidthInLan": false, - "minHomeDiskFreePct": 1, - "releasesURL": "https://upgrades.syncthing.net/meta.json", - "alwaysLocalNets": [], - "overwriteRemoteDeviceNamesOnConnect": false, - "tempIndexMinBlocks": 10 - }, - "ignoredDevices": [], - "ignoredFolders": [] -} \ No newline at end of file diff --git a/next-gen-gui/src/app/mocks/mock-system-connections.ts b/next-gen-gui/src/app/mocks/mock-system-connections.ts deleted file mode 100644 index 4e77f506f..000000000 --- a/next-gen-gui/src/app/mocks/mock-system-connections.ts +++ /dev/null @@ -1,44 +0,0 @@ -export const connections = { - "total": { - "paused": false, - "clientVersion": "", - "at": "2015-11-07T17:29:47.691637262+01:00", - "connected": false, - "inBytesTotal": 1479, - "type": "", - "outBytesTotal": 1318, - "address": "" - }, - "connections": { - "YZJBJFX-RDBL7WY-6ZGKJ2D-4MJB4E7-ZATSDUY-LD6Y3L3-MLFUYWE-AEMXJAC": { - "connected": true, - "inBytesTotal": 556, - "paused": false, - "at": "2015-11-07T17:29:47.691548971+01:00", - "clientVersion": "v0.12.1", - "address": "127.0.0.1:22002", - "type": "TCP (Client)", - "outBytesTotal": 550 - }, - "DOVII4U-SQEEESM-VZ2CVTC-CJM4YN5-QNV7DCU-5U3ASRL-YVFG6TH-W5DV5AA": { - "outBytesTotal": 0, - "type": "", - "address": "", - "at": "0001-01-01T00:00:00Z", - "clientVersion": "", - "paused": false, - "inBytesTotal": 0, - "connected": false - }, - "UYGDMA4-TPHOFO5-2VQYDCC-7CWX7XW-INZINQT-LE4B42N-4JUZTSM-IWCSXA4": { - "address": "", - "type": "", - "outBytesTotal": 0, - "connected": false, - "inBytesTotal": 0, - "paused": false, - "at": "0001-01-01T00:00:00Z", - "clientVersion": "" - } - } -} \ No newline at end of file diff --git a/next-gen-gui/src/app/mocks/mock-system-status.ts b/next-gen-gui/src/app/mocks/mock-system-status.ts deleted file mode 100644 index 1a6472f19..000000000 --- a/next-gen-gui/src/app/mocks/mock-system-status.ts +++ /dev/null @@ -1,59 +0,0 @@ -export const systemStatus = { - "alloc": 30618136, - "connectionServiceStatus": { - "dynamic+https://relays.syncthing.net/endpoint": { - "error": null, - "lanAddresses": [ - "relay://23.92.71.120:443/?id=53STGR7-YBM6FCX-PAZ2RHM-YPY6OEJ-WYHVZO7-PCKQRCK-PZLTP7T-434XCAD&pingInterval=1m0s&networkTimeout=2m0s&sessionLimitBps=0&globalLimitBps=0&statusAddr=:22070&providedBy=canton7" - ], - "wanAddresses": [ - "relay://23.92.71.120:443/?id=53STGR7-YBM6FCX-PAZ2RHM-YPY6OEJ-WYHVZO7-PCKQRCK-PZLTP7T-434XCAD&pingInterval=1m0s&networkTimeout=2m0s&sessionLimitBps=0&globalLimitBps=0&statusAddr=:22070&providedBy=canton7" - ] - }, - "tcp://0.0.0.0:22000": { - "error": null, - "lanAddresses": [ - "tcp://0.0.0.0:22000" - ], - "wanAddresses": [ - "tcp://0.0.0.0:22000" - ] - } - }, - "cpuPercent": 0, - "discoveryEnabled": true, - "discoveryErrors": { - "global@https://discovery-v4-1.syncthing.net/v2/": "500 Internal Server Error", - "global@https://discovery-v4-2.syncthing.net/v2/": "Post https://discovery-v4-2.syncthing.net/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)", - "global@https://discovery-v4-3.syncthing.net/v2/": "Post https://discovery-v4-3.syncthing.net/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)", - "global@https://discovery-v6-1.syncthing.net/v2/": "Post https://discovery-v6-1.syncthing.net/v2/: dial tcp [2001:470:28:4d6::5]:443: connect: no route to host", - "global@https://discovery-v6-2.syncthing.net/v2/": "Post https://discovery-v6-2.syncthing.net/v2/: dial tcp [2604:a880:800:10::182:a001]:443: connect: no route to host", - "global@https://discovery-v6-3.syncthing.net/v2/": "Post https://discovery-v6-3.syncthing.net/v2/: dial tcp [2400:6180:0:d0::d9:d001]:443: connect: no route to host" - }, - "discoveryMethods": 8, - "goroutines": 49, - "lastDialStatus": { - "tcp://10.20.30.40": { - "when": "2019-05-16T07:41:23Z", - "error": "dial tcp 10.20.30.40:22000: i/o timeout" - }, - "tcp://172.16.33.3:22000": { - "when": "2019-05-16T07:40:43Z", - "ok": true - }, - "tcp://83.233.120.221:22000": { - "when": "2019-05-16T07:41:13Z", - "error": "dial tcp 83.233.120.221:22000: connect: connection refused" - } - }, - "myID": "YZJBJFX-RDBL7WY-6ZGKJ2D-4MJB4E7-ZATSDUY-LD6Y3L3-MLFUYWE-AEMXJAC", - "pathSeparator": "/", - "startTime": "2016-06-06T19:41:43.039284753+02:00", - "sys": 42092792, - "themes": [ - "default", - "dark" - ], - "tilde": "/Users/jb", - "uptime": 2635 -} \ No newline at end of file diff --git a/next-gen-gui/src/app/services/cookie.service.spec.ts b/next-gen-gui/src/app/services/cookie.service.spec.ts deleted file mode 100644 index 43ea274f5..000000000 --- a/next-gen-gui/src/app/services/cookie.service.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { CookieService } from './cookie.service'; - -describe('CookieService', () => { - let service: CookieService; - - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(CookieService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/services/cookie.service.ts b/next-gen-gui/src/app/services/cookie.service.ts deleted file mode 100644 index f6d3cf467..000000000 --- a/next-gen-gui/src/app/services/cookie.service.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Injectable } from '@angular/core'; - -@Injectable({ - providedIn: 'root' -}) -export class CookieService { - - constructor() { } - - getCookie(name: string): string { - let ca: Array = document.cookie.split(';'); - let caLen: number = ca.length; - let cookieName = `${name}=`; - let c: string; - - for (let i: number = 0; i < caLen; i += 1) { - c = ca[i].replace(/^\s+/g, ''); - if (c.indexOf(cookieName) == 0) { - return c.substring(cookieName.length, c.length); - } - } - return ''; - } - - deleteCookie(name): void { - this.setCookie(name, "", -1); - } - - setCookie(name: string, value: string, expireDays: number, path: string = ""): void { - let d: Date = new Date(); - d.setTime(d.getTime() + expireDays * 24 * 60 * 60 * 1000); - let expires: string = "expires=" + d.toUTCString(); - document.cookie = name + "=" + value + "; " + expires + (path.length > 0 ? "; path=" + path : ""); - } -} diff --git a/next-gen-gui/src/app/services/db-completion.service.spec.ts b/next-gen-gui/src/app/services/db-completion.service.spec.ts deleted file mode 100644 index e6a07d016..000000000 --- a/next-gen-gui/src/app/services/db-completion.service.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { DbCompletionService } from './db-completion.service'; -import { HttpClientModule } from '@angular/common/http'; - -describe('DbCompletionService', () => { - let service: DbCompletionService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientModule], - providers: [DbCompletionService] - }); - service = TestBed.inject(DbCompletionService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/services/db-completion.service.ts b/next-gen-gui/src/app/services/db-completion.service.ts deleted file mode 100644 index e3dffa7c6..000000000 --- a/next-gen-gui/src/app/services/db-completion.service.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient, HttpParams } from '@angular/common/http'; -import { environment } from 'src/environments/environment'; -import { apiURL } from '../api-utils'; -import { Completion } from '../completion'; -import { map } from 'rxjs/operators'; -import { Observable } from 'rxjs'; -import { StType } from '../type'; - -@Injectable({ - providedIn: 'root' -}) -export class DbCompletionService { - private dbStatusUrl = environment.production ? apiURL + 'rest/db/completion' : 'api/dbCompletion'; - - constructor(private http: HttpClient) { } - - getCompletion(type: StType, id: string): Observable { - let httpOptions: { params: HttpParams }; - if (id) { - switch (type) { - case StType.Device: - httpOptions = { - params: new HttpParams().set('device', id) - }; - break; - case StType.Folder: - httpOptions = { - params: new HttpParams().set('folder', id) - }; - break; - } - } else { } - - return this.http - .get(this.dbStatusUrl, httpOptions) - .pipe( - map(res => { - // Remove from array in development - // in-memory-web-api returns arrays - if (!environment.production) { - const a: any = res as any; - if (a.length > 0) { - res = res[0]; - } - } - return res; - }) - ); - } -} diff --git a/next-gen-gui/src/app/services/db-status.service.spec.ts b/next-gen-gui/src/app/services/db-status.service.spec.ts deleted file mode 100644 index 65c7c871c..000000000 --- a/next-gen-gui/src/app/services/db-status.service.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { DbStatusService } from './db-status.service'; -import { HttpClientModule } from '@angular/common/http'; - -describe('DbStatusService', () => { - let service: DbStatusService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientModule], - providers: [DbStatusService] - }); - TestBed.configureTestingModule({}); - service = TestBed.inject(DbStatusService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/services/db-status.service.ts b/next-gen-gui/src/app/services/db-status.service.ts deleted file mode 100644 index 7c0bfe8d4..000000000 --- a/next-gen-gui/src/app/services/db-status.service.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; - -import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; - -import { environment } from '../../environments/environment' -import { apiURL } from '../api-utils' -import Folder from '../folder' - -@Injectable({ - providedIn: 'root' -}) -export class DbStatusService { - private dbStatusUrl = environment.production ? apiURL + 'rest/db/status' : 'api/dbStatus'; - - constructor(private http: HttpClient) { } - - getFolderStatus(id: string): Observable { - let httpOptions: { params: HttpParams }; - if (id) { - httpOptions = { - params: new HttpParams().set('folder', id) - }; - } else { } - - return this.http - .get(this.dbStatusUrl, httpOptions) - .pipe( - map(res => { - // Remove from array in development - // in-memory-web-api returns arrays - if (!environment.production) { - const a: any = res as any; - if (a.length > 0) { - res = res[0]; - } - } - return res; - }) - ); - } -} diff --git a/next-gen-gui/src/app/services/device.service.spec.ts b/next-gen-gui/src/app/services/device.service.spec.ts deleted file mode 100644 index 35b94a052..000000000 --- a/next-gen-gui/src/app/services/device.service.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { DeviceService } from './device.service'; -import { HttpClientModule } from '@angular/common/http'; - -describe('DeviceService', () => { - let service: DeviceService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientModule], - providers: [DeviceService] - }); - service = TestBed.inject(DeviceService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/services/device.service.ts b/next-gen-gui/src/app/services/device.service.ts deleted file mode 100644 index a1c0d995f..000000000 --- a/next-gen-gui/src/app/services/device.service.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { Injectable } from '@angular/core'; -import Device from '../device'; -import { Observable, Subscriber, ReplaySubject, Subject } from 'rxjs'; -import { SystemConfigService } from './system-config.service'; -import { SystemConnectionsService } from './system-connections.service'; -import { DbCompletionService } from './db-completion.service'; -import { SystemConnections } from '../connections'; -import { SystemStatusService } from './system-status.service'; -import { ProgressService } from './progress.service'; -import { StType } from '../type'; - -@Injectable({ - providedIn: 'root' -}) -export class DeviceService { - private devices: Device[]; - private sysConns: SystemConnections; - private devicesSubject: ReplaySubject = new ReplaySubject(1); - devicesUpdated$ = this.devicesSubject.asObservable(); - private thisDevice: Device; - - private deviceAddedSource = new Subject(); - deviceAdded$ = this.deviceAddedSource.asObservable(); - - constructor( - private systemConfigService: SystemConfigService, - private systemConnectionsService: SystemConnectionsService, - private dbCompletionService: DbCompletionService, - private systemStatusService: SystemStatusService, - private progressService: ProgressService, - ) { } - - getDeviceStatusInOrder(startIndex: number) { - // Return if there aren't any device at the index - if (startIndex >= (this.devices.length)) { - this.devicesSubject.next(this.devices); - // this.devicesSubject.complete(); - // this.deviceAddedSource.complete(); - return; - } - const device: Device = this.devices[startIndex]; - startIndex = startIndex + 1; - - // Check if device in the connections - if (this.sysConns.connections[device.deviceID] === undefined) { - device.stateType = Device.StateType.Unknown; - } else { - // Set connected - device.connected = this.sysConns.connections[device.deviceID].connected; - - // TODO ? temporarily set to connected - if (device.deviceID === this.thisDevice.deviceID) { - device.connected = true; - } - } - - this.dbCompletionService.getCompletion(StType.Device, device.deviceID).subscribe( - c => { - device.completion = c; - Device.recalcCompletion(device); - device.stateType = Device.getStateType(device); - device.state = Device.stateTypeToString(device.stateType); - - this.deviceAddedSource.next(device); - this.progressService.addToProgress(1); - - // recursively get the status of the next device - this.getDeviceStatusInOrder(startIndex); - }); - } - - /** - * getEach() returns each device - */ - requestDevices() { - this.systemConfigService.getDevices().subscribe( - devices => { - this.devices = devices; - - // First check to see which device is local 'thisDevice' - this.systemStatusService.getSystemStatus().subscribe( - status => { - this.devices.forEach(device => { - if (device.deviceID === status.myID) { - // TODO Determine if it should ignore thisDevice - this.thisDevice = device; - } - }); - - // Check folder devices to see if the device is used - this.systemConfigService.getFolders().subscribe( - folders => { - // Loop through all folder devices to see if the device is used - this.devices.forEach(device => { - // Alloc array if needed - if (!device.folders) { - device.folders = []; - } - - folders.forEach(folder => { - folder.devices.forEach(fdevice => { - if (device.deviceID === fdevice.deviceID) { - // The device is used by a folder - device.used = true; - - // Add a reference to the folder to the device - device.folders.push(folder); - } - }); - }); - }); - - // See if the connection is connected or undefined - this.systemConnectionsService.getSystemConnections().subscribe( - c => { - this.sysConns = c; - - // Synchronously get the status of each device - this.getDeviceStatusInOrder(0); - } - ); - }); - } - ) - } - ); - } -} diff --git a/next-gen-gui/src/app/services/filter.service.spec.ts b/next-gen-gui/src/app/services/filter.service.spec.ts deleted file mode 100644 index 279827c5c..000000000 --- a/next-gen-gui/src/app/services/filter.service.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { FilterService } from './filter.service'; - -describe('FilterService', () => { - let service: FilterService; - - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(FilterService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/services/filter.service.ts b/next-gen-gui/src/app/services/filter.service.ts deleted file mode 100644 index a30fe9a42..000000000 --- a/next-gen-gui/src/app/services/filter.service.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Injectable } from '@angular/core'; -import { StType } from '../type'; -import { Subject } from 'rxjs'; - -export interface FilterInput { - type: StType; - text: string -} - -@Injectable({ - providedIn: 'root' -}) -export class FilterService { - previousInputs = new Map( - [ - [StType.Folder, ""], - [StType.Device, ""], - ] - ) - - constructor() { } - - private filterChangeSource = new Subject(); - filterChanged$ = this.filterChangeSource.asObservable(); - - changeFilter(input: FilterInput) { - this.previousInputs.set(input.type, input.text) - this.filterChangeSource.next(input); - } -} diff --git a/next-gen-gui/src/app/services/folder.service.spec.ts b/next-gen-gui/src/app/services/folder.service.spec.ts deleted file mode 100644 index 6382d4ac8..000000000 --- a/next-gen-gui/src/app/services/folder.service.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { FolderService } from './folder.service'; -import { HttpClientModule } from '@angular/common/http'; - -describe('FolderService', () => { - let service: FolderService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientModule], - providers: [FolderService] - }); - service = TestBed.inject(FolderService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/services/folder.service.ts b/next-gen-gui/src/app/services/folder.service.ts deleted file mode 100644 index d31683edd..000000000 --- a/next-gen-gui/src/app/services/folder.service.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { Injectable } from '@angular/core'; -import { SystemConfigService } from './system-config.service'; -import { Observable, Subscriber, Subject, ReplaySubject } from 'rxjs'; -import Folder from '../folder'; -import { DbStatusService } from './db-status.service'; -import { ProgressService } from './progress.service'; -import { DbCompletionService } from './db-completion.service'; -import { StType } from '../type'; -import { DeviceService } from './device.service'; - -@Injectable({ - providedIn: 'root' -}) -export class FolderService { - private folders: Folder[]; - private foldersSubject: ReplaySubject = new ReplaySubject(1); - foldersUpdated$ = this.foldersSubject.asObservable(); - private folderAddedSource = new Subject(); - folderAdded$ = this.folderAddedSource.asObservable(); - - constructor( - private systemConfigService: SystemConfigService, - private deviceService: DeviceService, - private dbStatusService: DbStatusService, - private dbCompletionService: DbCompletionService, - private progressService: ProgressService, - ) { } - - getFolderStatusInOrder(startIndex: number) { - // Return if there aren't any folders at the index - if (startIndex >= (this.folders.length)) { - this.foldersSubject.next(this.folders); - // this.folderAddedSource.complete(); - return; - } - const folder: Folder = this.folders[startIndex]; - startIndex = startIndex + 1; - - // Folder devices array only has deviceID - // and we want all the device info - this.systemConfigService.getDevices().subscribe( - devices => { - devices.forEach(device => { - // Update any device this folder - // has reference to - folder.devices.forEach((folderDevice, index) => { - if (folderDevice.deviceID === device.deviceID) { - console.log("find device match?", device.name) - folder.devices[index] = device; - - console.log("update?", folder.devices); - } - }); - }); - - // Gather the folder information from the status and - // completion services - this.dbStatusService.getFolderStatus(folder.id).subscribe( - status => { - folder.status = status; - - this.dbCompletionService.getCompletion(StType.Folder, folder.id).subscribe( - c => { - folder.completion = c; - folder.stateType = Folder.getStateType(folder); - folder.state = Folder.stateTypeToString(folder.stateType); - - this.folderAddedSource.next(folder); - this.progressService.addToProgress(1); - - // Now that we have all the folder information - // recursively get the status of the next folder - this.getFolderStatusInOrder(startIndex); - }); - } - ); - } - ) - - } - - /** - * requestFolders() requests each folder and uses db status service to - * set all their statuses and db completion service to find - * completion in order. Updating folderAdded$ and foldersUpdate$ - * observers - */ - requestFolders() { - this.systemConfigService.getFolders().subscribe( - folders => { - this.folders = folders; - - // Synchronously get the status of each folder - this.getFolderStatusInOrder(0); - } - ); - } -} \ No newline at end of file diff --git a/next-gen-gui/src/app/services/in-memory-config-data.service.spec.ts b/next-gen-gui/src/app/services/in-memory-config-data.service.spec.ts deleted file mode 100644 index 4650f227e..000000000 --- a/next-gen-gui/src/app/services/in-memory-config-data.service.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { InMemoryConfigDataService } from './in-memory-config-data.service'; - -describe('InMemoryDataService', () => { - let service: InMemoryConfigDataService; - - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(InMemoryConfigDataService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/services/in-memory-config-data.service.ts b/next-gen-gui/src/app/services/in-memory-config-data.service.ts deleted file mode 100644 index d44fbb50c..000000000 --- a/next-gen-gui/src/app/services/in-memory-config-data.service.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Injectable } from '@angular/core'; -import { config } from '../mocks/mock-system-config'; -import { dbStatus } from '../mocks/mock-db-status'; -import { connections } from '../mocks/mock-system-connections'; -import { dbCompletion } from '../mocks/mock-db-completion'; -import { systemStatus } from '../mocks/mock-system-status'; - -@Injectable({ - providedIn: 'root' -}) -export class InMemoryConfigDataService { - createDb() { - return { config, dbStatus, connections, dbCompletion, systemStatus }; - } - - constructor() { } -} diff --git a/next-gen-gui/src/app/services/message.service.spec.ts b/next-gen-gui/src/app/services/message.service.spec.ts deleted file mode 100644 index 1db761b5e..000000000 --- a/next-gen-gui/src/app/services/message.service.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { MessageService } from './message.service'; - -describe('MessageService', () => { - let service: MessageService; - - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(MessageService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/services/message.service.ts b/next-gen-gui/src/app/services/message.service.ts deleted file mode 100644 index 39dc5d954..000000000 --- a/next-gen-gui/src/app/services/message.service.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Injectable } from '@angular/core'; -import { Subject } from 'rxjs'; - -@Injectable({ - providedIn: 'root' -}) -export class MessageService { - messages: string[] = []; - private messageAddedSource = new Subject(); - messageAdded$ = this.messageAddedSource.asObservable(); - - add(message: string) { - this.messages.push(message); - this.messageAddedSource.next(message); - } - - clear() { - this.messages = []; - } -} \ No newline at end of file diff --git a/next-gen-gui/src/app/services/progress.service.spec.ts b/next-gen-gui/src/app/services/progress.service.spec.ts deleted file mode 100644 index 9b0f96bcc..000000000 --- a/next-gen-gui/src/app/services/progress.service.spec.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { ProgressService } from './progress.service'; -import { stringToKeyValue } from '@angular/flex-layout/extended/typings/style/style-transforms'; - -describe('ProgressService', () => { - let service: ProgressService; - - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(ProgressService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); - - it('#percentValue should return 0 - 100', () => { - interface iTest { - total: number, - progress: number, - expected: number, - } - const tests: Map = new Map([ - ["default", { total: 0, progress: 0, expected: 0 }], - ["NaN return 0", { total: 0, progress: 100, expected: 0 }], - ["greater than 100 return 100", { total: 10, progress: 100, expected: 100 }], - ["valid", { total: 100, progress: 100, expected: 100 }], - ["valid", { total: 100, progress: 50, expected: 50 }], - ["test floor", { total: 133, progress: 41, expected: 30 }], - ]); - - service = new ProgressService(); - for (let test of tests.values()) { - service.total = test.total; - service.updateProgress(test.progress); - expect(service.percentValue).toBe(test.expected); - } - }); -}); diff --git a/next-gen-gui/src/app/services/progress.service.ts b/next-gen-gui/src/app/services/progress.service.ts deleted file mode 100644 index 953b56810..000000000 --- a/next-gen-gui/src/app/services/progress.service.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Injectable } from '@angular/core'; - -@Injectable({ - providedIn: 'root' -}) -export class ProgressService { - private progress: number = 0; - private _total: number = 0; - set total(t: number) { - this._total = t; - } - - get percentValue(): number { - let p: number = Math.floor((this.progress / this._total) * 100); - if (p < 0 || isNaN(p) || p === Infinity) { - p = 0; - } else if (p > 100) { - p = 100; - } - return p; - } - - constructor() { } - - addToProgress(n: number) { - if (n < 0 || isNaN(n) || n === Infinity) { - n = 0; - } - - this.progress += n; - } - - updateProgress(n: number) { - if (n < 0 || isNaN(n) || n === Infinity) { - n = 0 - } else if (n > 100) { - n = 100 - } - - this.progress = n; - } - - isComplete(): boolean { - if (this.progress >= this._total && this.progress > 0 && this._total > 0) { - return true; - } - - return false; - } -} \ No newline at end of file diff --git a/next-gen-gui/src/app/services/request-cache.service.spec.ts b/next-gen-gui/src/app/services/request-cache.service.spec.ts deleted file mode 100644 index 4d65caa17..000000000 --- a/next-gen-gui/src/app/services/request-cache.service.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { RequestCacheService } from './request-cache.service'; - -describe('RequestCacheService', () => { - let service: RequestCacheService; - - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(RequestCacheService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/services/request-cache.service.ts b/next-gen-gui/src/app/services/request-cache.service.ts deleted file mode 100644 index 2396378b6..000000000 --- a/next-gen-gui/src/app/services/request-cache.service.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpResponse, HttpRequest } from '@angular/common/http'; - -export interface RequestCacheEntry { - url: string; - response: HttpResponse; - lastRead: number; -} - -const maxAge = 30000; // milliseconds - -@Injectable({ - providedIn: 'root' -}) -export class RequestCacheService { - private cache: Map = new Map(); - - constructor() { } - - get(req: HttpRequest): HttpResponse | undefined { - const url = req.urlWithParams; - const cached = this.cache.get(url); - - if (!cached) { - return undefined; - } - - const isExpired = cached.lastRead < (Date.now() - maxAge); - return isExpired ? undefined : cached.response; - } - - put(req: HttpRequest, response: HttpResponse): void { - const url = req.urlWithParams; - - const entry = { url, response, lastRead: Date.now() }; - this.cache.set(url, entry); - - // Remove expired cache entries - const expired = Date.now() - maxAge; - this.cache.forEach(entry => { - if (entry.lastRead < expired) { - this.cache.delete(entry.url); - } - }); - } - - clearAll(): void { - this.cache = new Map(); - } -} diff --git a/next-gen-gui/src/app/services/system-config.service.spec.ts b/next-gen-gui/src/app/services/system-config.service.spec.ts deleted file mode 100644 index 011706140..000000000 --- a/next-gen-gui/src/app/services/system-config.service.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { SystemConfigService } from './system-config.service'; -import { HttpClientModule } from '@angular/common/http'; - -describe('SystemConfigService', () => { - let service: SystemConfigService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientModule], - providers: [SystemConfigService] - }); - service = TestBed.inject(SystemConfigService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/services/system-config.service.ts b/next-gen-gui/src/app/services/system-config.service.ts deleted file mode 100644 index f18ba79fd..000000000 --- a/next-gen-gui/src/app/services/system-config.service.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; - -import { Observable, ReplaySubject } from 'rxjs'; -import { map } from 'rxjs/operators'; - -import Folder from '../folder'; -import Device from '../device'; -import { environment } from '../../environments/environment' -import { apiURL } from '../api-utils' -import { ProgressService } from './progress.service'; - -@Injectable({ - providedIn: 'root' -}) -export class SystemConfigService { - private folders: Folder[]; - private devices: Device[]; - private foldersSubject: ReplaySubject = new ReplaySubject(1); - private devicesSubject: ReplaySubject = new ReplaySubject(1); - - private systemConfigUrl = environment.production ? apiURL + 'rest/system/config' : 'api/config'; - private guiConfigUrl = environment.production ? apiURL + 'rest/config/gui' : 'api/config/gui'; - - constructor( - private http: HttpClient, - private progressService: ProgressService, - ) { } - - getSystemConfig(): Observable { - return this.http - .get(this.systemConfigUrl) - .pipe( - map(res => { - this.folders = res['folders']; - this.devices = res['devices']; - - // Set the total for the progress service - this.progressService.total = this.folders.length + this.devices.length; - - this.foldersSubject.next(this.folders); - this.devicesSubject.next(this.devices); - - return res; - }) - ); - } - - getFolders(): Observable { - return this.foldersSubject.asObservable(); - } - - getDevices(): Observable { - return this.devicesSubject.asObservable(); - } - - setGUITheme(theme: String): Observable { - return this.http.patch(this.guiConfigUrl, { theme: theme }) - } -} \ No newline at end of file diff --git a/next-gen-gui/src/app/services/system-connections.service.spec.ts b/next-gen-gui/src/app/services/system-connections.service.spec.ts deleted file mode 100644 index c55566e12..000000000 --- a/next-gen-gui/src/app/services/system-connections.service.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { SystemConnectionsService } from './system-connections.service'; -import { HttpClientModule } from '@angular/common/http'; - -describe('SystemConnectionsService', () => { - let service: SystemConnectionsService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientModule], - providers: [SystemConnectionsService] - }); - service = TestBed.inject(SystemConnectionsService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/services/system-connections.service.ts b/next-gen-gui/src/app/services/system-connections.service.ts deleted file mode 100644 index d196da228..000000000 --- a/next-gen-gui/src/app/services/system-connections.service.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Injectable } from '@angular/core'; -import { environment } from 'src/environments/environment'; -import { apiURL } from '../api-utils'; -import { HttpClient } from '@angular/common/http'; -import { map } from 'rxjs/operators'; -import { Observable } from 'rxjs'; -import { SystemConnections } from '../connections'; - -@Injectable({ - providedIn: 'root' -}) -export class SystemConnectionsService { - private systemConfigUrl = environment.production ? apiURL + 'rest/system/connections' : 'api/connections'; - - constructor(private http: HttpClient) { } - - getSystemConnections(): Observable { - return this.http - .get(this.systemConfigUrl) - .pipe( - map(res => { - return res; - }) - ); - } -} diff --git a/next-gen-gui/src/app/services/system-status.service.spec.ts b/next-gen-gui/src/app/services/system-status.service.spec.ts deleted file mode 100644 index a292daa68..000000000 --- a/next-gen-gui/src/app/services/system-status.service.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { SystemStatusService } from './system-status.service'; -import { HttpClientModule } from '@angular/common/http'; - -describe('SystemStatusService', () => { - let service: SystemStatusService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientModule], - providers: [SystemStatusService] - }); - service = TestBed.inject(SystemStatusService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/services/system-status.service.ts b/next-gen-gui/src/app/services/system-status.service.ts deleted file mode 100644 index ca9a18bbe..000000000 --- a/next-gen-gui/src/app/services/system-status.service.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Injectable } from '@angular/core'; -import { environment } from 'src/environments/environment'; -import { apiURL } from '../api-utils'; -import { Observable } from 'rxjs'; -import { HttpClient } from '@angular/common/http'; -import { map } from 'rxjs/operators'; -import { SystemStatus } from '../system-status'; - -@Injectable({ - providedIn: 'root' -}) -export class SystemStatusService { - - private systemStatusUrl = environment.production ? apiURL + 'rest/system/status' : 'api/systemStatus'; - - constructor(private http: HttpClient) { } - - getSystemStatus(): Observable { - return this.http - .get(this.systemStatusUrl) - .pipe( - map(res => { - return res; - }) - ); - } -} diff --git a/next-gen-gui/src/app/style.ts b/next-gen-gui/src/app/style.ts deleted file mode 100644 index 7caaba665..000000000 --- a/next-gen-gui/src/app/style.ts +++ /dev/null @@ -1,11 +0,0 @@ -// material design elevation for all dashboard components -export const cardElevation: string = "mat-elevation-z2"; -export const dataTableElevation: string = "mat-elevation-z0"; - -export const colors: Map = new Map([ - ["blue", "#0891D1"], - ["yellow", "#FFC400"], - ["green", "#56C568"], - ["grey", "#DDDDDD"], - ["red", "#EB5757"] -]); \ No newline at end of file diff --git a/next-gen-gui/src/app/system-status.ts b/next-gen-gui/src/app/system-status.ts deleted file mode 100644 index 5f4be45ed..000000000 --- a/next-gen-gui/src/app/system-status.ts +++ /dev/null @@ -1,17 +0,0 @@ -export interface SystemStatus { - alloc: number; - connectionServiceStatus: any; - cpuPercent: number; // allows returns 0 - discoveryEnabled: boolean; - discoveryErrors: any; - discoveryMethods: number; - goroutines: number; - lastDialStatus: any; - myID: string; - pathSeparator: string; - startTime: string; - sys: number; - themes: string[]; - tilde: string; - uptime: number; -} \ No newline at end of file diff --git a/next-gen-gui/src/app/trim.pipe.spec.ts b/next-gen-gui/src/app/trim.pipe.spec.ts deleted file mode 100644 index 200699000..000000000 --- a/next-gen-gui/src/app/trim.pipe.spec.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { TrimPipe } from './trim.pipe'; - -describe('TrimPipe', () => { - it('create an instance', () => { - const pipe = new TrimPipe(); - expect(pipe).toBeTruthy(); - }); -}); diff --git a/next-gen-gui/src/app/trim.pipe.ts b/next-gen-gui/src/app/trim.pipe.ts deleted file mode 100644 index d922492cf..000000000 --- a/next-gen-gui/src/app/trim.pipe.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Pipe, PipeTransform } from '@angular/core'; - -@Pipe({ - name: 'trim' -}) -export class TrimPipe implements PipeTransform { - - transform(value: string, ...args: string[]): string { - return value.trim(); - } - -} diff --git a/next-gen-gui/src/app/type.ts b/next-gen-gui/src/app/type.ts deleted file mode 100644 index 408bfb869..000000000 --- a/next-gen-gui/src/app/type.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum StType { - Folder = 1, - Device, -} \ No newline at end of file diff --git a/next-gen-gui/src/assets/.gitkeep b/next-gen-gui/src/assets/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/next-gen-gui/src/assets/logo-horizontal.svg b/next-gen-gui/src/assets/logo-horizontal.svg deleted file mode 100644 index de8850f53..000000000 --- a/next-gen-gui/src/assets/logo-horizontal.svg +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/next-gen-gui/src/environments/environment.prod.ts b/next-gen-gui/src/environments/environment.prod.ts deleted file mode 100644 index 3612073bc..000000000 --- a/next-gen-gui/src/environments/environment.prod.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const environment = { - production: true -}; diff --git a/next-gen-gui/src/environments/environment.ts b/next-gen-gui/src/environments/environment.ts deleted file mode 100644 index 7b4f817ad..000000000 --- a/next-gen-gui/src/environments/environment.ts +++ /dev/null @@ -1,16 +0,0 @@ -// This file can be replaced during build by using the `fileReplacements` array. -// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. -// The list of file replacements can be found in `angular.json`. - -export const environment = { - production: false -}; - -/* - * For easier debugging in development mode, you can import the following file - * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. - * - * This import should be commented out in production mode because it will have a negative impact - * on performance if an error is thrown. - */ -// import 'zone.js/dist/zone-error'; // Included with Angular CLI. diff --git a/next-gen-gui/src/favicon.ico b/next-gen-gui/src/favicon.ico deleted file mode 100644 index 997406ad2..000000000 Binary files a/next-gen-gui/src/favicon.ico and /dev/null differ diff --git a/next-gen-gui/src/index.html b/next-gen-gui/src/index.html deleted file mode 100644 index 1f9d1b7bd..000000000 --- a/next-gen-gui/src/index.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - TechUi - - - - - - - -
-
-
- - - - \ No newline at end of file diff --git a/next-gen-gui/src/main.ts b/next-gen-gui/src/main.ts deleted file mode 100644 index c7b673cf4..000000000 --- a/next-gen-gui/src/main.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { enableProdMode } from '@angular/core'; -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; - -import { AppModule } from './app/app.module'; -import { environment } from './environments/environment'; - -if (environment.production) { - enableProdMode(); -} - -platformBrowserDynamic().bootstrapModule(AppModule) - .catch(err => console.error(err)); diff --git a/next-gen-gui/src/polyfills.ts b/next-gen-gui/src/polyfills.ts deleted file mode 100644 index 03711e5d9..000000000 --- a/next-gen-gui/src/polyfills.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * This file includes polyfills needed by Angular and is loaded before the app. - * You can add your own extra polyfills to this file. - * - * This file is divided into 2 sections: - * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. - * 2. Application imports. Files imported after ZoneJS that should be loaded before your main - * file. - * - * The current setup is for so-called "evergreen" browsers; the last versions of browsers that - * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), - * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. - * - * Learn more in https://angular.io/guide/browser-support - */ - -/*************************************************************************************************** - * BROWSER POLYFILLS - */ - -/** IE10 and IE11 requires the following for NgClass support on SVG elements */ -// import 'classlist.js'; // Run `npm install --save classlist.js`. - -/** - * Web Animations `@angular/platform-browser/animations` - * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. - * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). - */ -// import 'web-animations-js'; // Run `npm install --save web-animations-js`. - -/** - * By default, zone.js will patch all possible macroTask and DomEvents - * user can disable parts of macroTask/DomEvents patch by setting following flags - * because those flags need to be set before `zone.js` being loaded, and webpack - * will put import in the top of bundle, so user need to create a separate file - * in this directory (for example: zone-flags.ts), and put the following flags - * into that file, and then add the following code before importing zone.js. - * import './zone-flags'; - * - * The flags allowed in zone-flags.ts are listed here. - * - * The following flags will work for all browsers. - * - * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame - * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick - * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames - * - * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js - * with the following flag, it will bypass `zone.js` patch for IE/Edge - * - * (window as any).__Zone_enable_cross_context_check = true; - * - */ - -/*************************************************************************************************** - * Zone JS is required by default for Angular itself. - */ -import 'zone.js/dist/zone'; // Included with Angular CLI. - - -/*************************************************************************************************** - * APPLICATION IMPORTS - */ diff --git a/next-gen-gui/src/styles.scss b/next-gen-gui/src/styles.scss deleted file mode 100644 index 15bed241b..000000000 --- a/next-gen-gui/src/styles.scss +++ /dev/null @@ -1,140 +0,0 @@ -// Custom Theming for Angular Material -// For more information: https://material.angular.io/guide/theming -@import '~@angular/material/theming'; -@import './app/card/card.component.scss'; -@import './app/charts/chart-item/chart-item.component.scss'; -@import './app/lists/status-list/status-list.component.scss'; - - -// Custom typography -$tech-ui-typography: mat-typography-config( - $font-family: '"Lucida Console", Monaco, monospace', - $display-4: mat-typography-level(112px, 112px, 300), - $display-3: mat-typography-level(56px, 56px, 400), - $display-2: mat-typography-level(45px, 48px, 400), - $display-1: mat-typography-level(34px, 40px, 400), - $headline: mat-typography-level(24px, 32px, 400), - $title: mat-typography-level(20px, 32px, 500), - $subheading-2: mat-typography-level(16px, 28px, 400), - $subheading-1: mat-typography-level(15px, 24px, 400), - $body-2: mat-typography-level(14px, 24px, 500), - $body-1: mat-typography-level(14px, 20px, 400), - $caption: mat-typography-level(12px, 20px, 400), - $button: mat-typography-level(14px, 14px, 500), - // Line-height must be unit-less fraction of the font-size. - $input: mat-typography-level(16px, 1.25, 400) -); - -// Include the common styles for Angular Material. We include this here so that you only -// have to load a single css file for Angular Material in your app. -// Be sure that you only ever include this mixin once! -@include mat-core($tech-ui-typography); - -// Define the palettes for your theme using the Material Design palettes available in palette.scss -// (imported above). For each palette, you can optionally specify a default, lighter, and darker -// hue. Available color palettes: https://material.io/design/color/ -$tech-ui-blue: ( - 50: #a5e0fc, - 100: #8fd5f7, - 200: #74c7ef, - 300: #51beed, - 400: #3eafe3, - 500: #0891D1, - 600: #0891D1, - 700: #0891D1, - 800: #0891D1, - 900: #0891D1, - A100: #0891D1, - A200: #0891D1, - A400: #0891D1, - A700: #0891D1, - contrast: ( - 50: $dark-primary-text, - 100: $dark-primary-text, - 200: $dark-primary-text, - 300: $dark-primary-text, - 400: $dark-primary-text, - 500: $light-primary-text, - 600: $light-primary-text, - 700: $light-primary-text, - 800: $light-primary-text, - 900: $light-primary-text, - A100: $dark-primary-text, - A200: $light-primary-text, - A400: $light-primary-text, - A700: $light-primary-text, - ) -); - -$tech-ui-primary: mat-palette($tech-ui-blue); -$tech-ui-accent: mat-palette($tech-ui-blue, A200, A100, A400); - -// The warn palette is optional (defaults to red). -$tech-ui-warn: mat-palette($mat-red); - -// Create the theme object (a Sass map containing all of the palettes). -$tech-ui-theme: mat-light-theme($tech-ui-primary, $tech-ui-accent, $tech-ui-warn); -$tech-ui-dark-theme: mat-dark-theme($tech-ui-primary, $tech-ui-accent, $tech-ui-warn); - -// Include theme styles for core and each component used in your app. -// Alternatively, you can import and @include the theme mixins for each component -// that you are using. -@include angular-material-theme($tech-ui-theme); -@include tui-card-theme($tech-ui-theme); -@include chart-item-theme($tech-ui-theme); - -html, body { - height: 100%; - background-color: #eeeeee; - font-size: mat-font-size($tech-ui-typography, body-1); - font-family: mat-font-family($tech-ui-typography); - line-height: mat-line-height($tech-ui-typography, body-1); - color: #303030; -} - -a { - text-decoration: underline; - color: #303030; -} - -@media (prefers-color-scheme: dark) { - @include angular-material-theme($tech-ui-dark-theme); - @include tui-card-theme($tech-ui-dark-theme); - @include chart-item-theme($tech-ui-dark-theme); - - html, body { - background-color: #303030; - color: white; - } - - a { - color: white; - } -} - -.status-list .tui-card .tui-card-content { - padding-top: 0; -} - -// Chart.js styles -#chartjs-tooltip { - /* max z-index value */ - z-index: 2147483647; - opacity: 0; - position: absolute; - background: rgba(0, 0, 0, .7); - color: white; - border-radius: 3px; - -webkit-transition: opacity .3s ease; - transition: opacity .3s ease; - pointer-events: none; - -webkit-transform: translate(-50%, 0); - transform: translate(-50%, 0); -} - -.chartjs-tooltip-key { - display: inline-block; - width: 10px; - height: 10px; - margin-right: 10px; -} \ No newline at end of file diff --git a/next-gen-gui/src/test.ts b/next-gen-gui/src/test.ts deleted file mode 100644 index 50193eb0f..000000000 --- a/next-gen-gui/src/test.ts +++ /dev/null @@ -1,25 +0,0 @@ -// This file is required by karma.conf.js and loads recursively all the .spec and framework files - -import 'zone.js/dist/zone-testing'; -import { getTestBed } from '@angular/core/testing'; -import { - BrowserDynamicTestingModule, - platformBrowserDynamicTesting -} from '@angular/platform-browser-dynamic/testing'; - -declare const require: { - context(path: string, deep?: boolean, filter?: RegExp): { - keys(): string[]; - (id: string): T; - }; -}; - -// First, initialize the Angular testing environment. -getTestBed().initTestEnvironment( - BrowserDynamicTestingModule, - platformBrowserDynamicTesting() -); -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().map(context); diff --git a/next-gen-gui/tsconfig.app.json b/next-gen-gui/tsconfig.app.json deleted file mode 100644 index f758d9820..000000000 --- a/next-gen-gui/tsconfig.app.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "./out-tsc/app", - "types": [] - }, - "files": [ - "src/main.ts", - "src/polyfills.ts" - ], - "include": [ - "src/**/*.d.ts" - ] -} diff --git a/next-gen-gui/tsconfig.json b/next-gen-gui/tsconfig.json deleted file mode 100644 index 8c4ef3bba..000000000 --- a/next-gen-gui/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "compileOnSave": false, - "compilerOptions": { - "baseUrl": "./", - "outDir": "./dist/out-tsc", - "sourceMap": true, - "declaration": false, - "downlevelIteration": true, - "experimentalDecorators": true, - "module": "esnext", - "moduleResolution": "node", - "importHelpers": true, - "target": "es2015", - "lib": [ - "es2018", - "dom" - ] - }, - "angularCompilerOptions": { - "fullTemplateTypeCheck": true, - "strictInjectionParameters": true - } -} diff --git a/next-gen-gui/tsconfig.spec.json b/next-gen-gui/tsconfig.spec.json deleted file mode 100644 index 6400fde7d..000000000 --- a/next-gen-gui/tsconfig.spec.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "./out-tsc/spec", - "types": [ - "jasmine", - "node" - ] - }, - "files": [ - "src/test.ts", - "src/polyfills.ts" - ], - "include": [ - "src/**/*.spec.ts", - "src/**/*.d.ts" - ] -} diff --git a/next-gen-gui/tslint.json b/next-gen-gui/tslint.json deleted file mode 100644 index c8d70f152..000000000 --- a/next-gen-gui/tslint.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "extends": "tslint:recommended", - "rules": { - "array-type": false, - "arrow-parens": false, - "deprecation": { - "severity": "warning" - }, - "component-class-suffix": true, - "contextual-lifecycle": true, - "directive-class-suffix": true, - "directive-selector": [ - true, - "attribute", - "app", - "camelCase" - ], - "component-selector": [ - true, - "element", - "app", - "kebab-case" - ], - "import-blacklist": [ - true, - "rxjs/Rx" - ], - "interface-name": false, - "max-classes-per-file": false, - "max-line-length": [ - true, - 140 - ], - "member-access": false, - "member-ordering": [ - true, - { - "order": [ - "static-field", - "instance-field", - "static-method", - "instance-method" - ] - } - ], - "no-consecutive-blank-lines": false, - "no-console": [ - true, - "debug", - "info", - "time", - "timeEnd", - "trace" - ], - "no-empty": false, - "no-inferrable-types": [ - true, - "ignore-params" - ], - "no-non-null-assertion": true, - "no-redundant-jsdoc": true, - "no-switch-case-fall-through": true, - "no-var-requires": false, - "object-literal-key-quotes": [ - true, - "as-needed" - ], - "object-literal-sort-keys": false, - "ordered-imports": false, - "quotemark": [ - true, - "single" - ], - "trailing-comma": false, - "no-conflicting-lifecycle": true, - "no-host-metadata-property": true, - "no-input-rename": true, - "no-inputs-metadata-property": true, - "no-output-native": true, - "no-output-on-prefix": true, - "no-output-rename": true, - "no-outputs-metadata-property": true, - "template-banana-in-box": true, - "template-no-negated-async": true, - "use-lifecycle-interface": true, - "use-pipe-transform-interface": true - }, - "rulesDirectory": [ - "codelyzer" - ] -} \ No newline at end of file diff --git a/proto/bep/bep.proto b/proto/bep/bep.proto index 4cac50597..0d6d9bd17 100644 --- a/proto/bep/bep.proto +++ b/proto/bep/bep.proto @@ -145,7 +145,7 @@ message BlockInfo { bytes hash = 3; int64 offset = 1; int32 size = 2; - uint32 weak_hash = 4; + reserved 4; } message Vector { @@ -203,8 +203,8 @@ message Request { int32 size = 5; bytes hash = 6; bool from_temporary = 7; - uint32 weak_hash = 8; int32 block_no = 9; + reserved 8; } // Response diff --git a/relnotes/v2.0.md b/relnotes/v2.0.md new file mode 100644 index 000000000..4a0bff505 --- /dev/null +++ b/relnotes/v2.0.md @@ -0,0 +1,38 @@ +## Major changes in 2.0 + +- Database backend switched from LevelDB to SQLite. There is a migration on + first launch which can be lengthy for larger setups. The new database is + easier to understand and maintain and, hopefully, less buggy. + +- Deleted items are no longer kept forever in the database, instead they are + forgotten after six months. If your use case require deletes to take + effect after more than a six month delay, set the + `--db-delete-retention-interval` command line option or corresponding + environment variable to zero, or a longer time interval of your choosing. + +- Modernised command line options parsing. Old single-dash long options are + no longer supported, e.g. `-home` must be given as `--home`. Some options + have been renamed, others have become subcommands. All serve options are + now also accepted as environment variables. See `syncthing --help` and + `syncthing serve --help` for details. + +- Rolling hash detection of shifted data is no longer supported as this + effectively never helped. Instead, scanning and syncing is faster and more + efficient without it. + +- A "default folder" is no longer created on first startup. + +- Multiple connections are now used by default between v2 devices. The new + default value is to use three connections: one for index metadata and two + for data exchange. + +- The following platforms unfortunately no longer get prebuilt binaries for + download at syncthing.net and on GitHub, due to complexities related to + cross compilation with SQLite: + + - dragonfly/amd64 + - illumos/amd64 and solaris/amd64 + - linux/ppc64 + - netbsd/* + - openbsd/386 and openbsd/arm + - windows/arm diff --git a/relnotes/v2.md b/relnotes/v2.md new file mode 100644 index 000000000..ed9126031 --- /dev/null +++ b/relnotes/v2.md @@ -0,0 +1,6 @@ +This release is also available as: + +* APT repository: https://apt.syncthing.net/ + +* Docker image: `docker.io/syncthing/syncthing:{{.version}}` or `ghcr.io/syncthing/syncthing:{{.version}}` + (`{docker,ghcr}.io/syncthing/syncthing:2` to follow just the major version) diff --git a/script/authors.go b/script/authors.go index ca128295d..cc2861c0f 100644 --- a/script/authors.go +++ b/script/authors.go @@ -16,7 +16,6 @@ import ( "bytes" "cmp" "fmt" - "io" "log" "math" "os" @@ -65,69 +64,33 @@ type author struct { func main() { // Read authors from the AUTHORS file - authors := getAuthors() - - // Grab the set of thus known email addresses - listed := make(stringSet) - names := make(map[string]int) - for i, a := range authors { - names[a.name] = i - for _, e := range a.emails { - listed.add(e) - } - } + authorSet := getAuthors() // Grab the set of all known authors based on the git log, and add any // missing ones to the authors list. - all := allAuthors() - for email, name := range all { - if listed.has(email) { - continue - } + addAuthors(authorSet) - if _, ok := names[name]; ok && name != "" { - // We found a match on name - authors[names[name]].emails = append(authors[names[name]].emails, email) - listed.add(email) - continue - } + authors := authorSet.filteredAuthors() - authors = append(authors, author{ - name: name, - emails: []string{email}, - }) - names[name] = len(authors) - 1 - listed.add(email) - } + // Write authors to the about dialog - // Write author names in GUI about modal - - getContributions(authors) - - // Sort by contributions slices.SortFunc(authors, func(a, b author) int { - // Sort first by log10(commits), then by name. This means that we first get - // an alphabetic list of people with >= 1000 commits, then a list of people - // with >= 100 commits, and so on. - if a.log10commits != b.log10commits { - return cmp.Compare(b.log10commits, a.log10commits) - } - return strings.Compare(a.name, b.name) + return cmp.Or( + -cmp.Compare(a.log10commits, b.log10commits), + cmp.Compare(strings.ToLower(a.name), strings.ToLower(b.name))) }) var lines []string for _, author := range authors { - if authorBotsRe.MatchString(author.name) { - // Only humans are eligible, pending future legislation to the - // contrary. - continue - } lines = append(lines, author.name) } replacement := strings.Join(lines, ", ") authorsRe := regexp.MustCompile(`(?s)id="contributor-list">.*?`) - bs := readAll(htmlFile) + bs, err := os.ReadFile(htmlFile) + if err != nil { + log.Fatal(err) + } bs = authorsRe.ReplaceAll(bs, []byte("id=\"contributor-list\">\n"+replacement+"\n ")) if err := os.WriteFile(htmlFile, bs, 0o644); err != nil { @@ -155,16 +118,22 @@ func main() { for _, email := range author.emails { fmt.Fprintf(out, " <%s>", email) } - fmt.Fprintf(out, "\n") + fmt.Fprint(out, "\n") } out.Close() } -func getAuthors() []author { - bs := readAll("AUTHORS") - lines := strings.Split(string(bs), "\n") - var authors []author +func getAuthors() *authorSet { + bs, err := os.ReadFile("AUTHORS") + if err != nil { + log.Fatal(err) + } + lines := strings.Split(string(bs), "\n") + authors := &authorSet{ + emails: make(map[string]int), + commits: make(map[string]stringSet), + } for _, line := range lines { if len(line) == 0 || line[0] == '#' { continue @@ -173,7 +142,9 @@ func getAuthors() []author { fields := strings.Fields(line) var author author for _, field := range fields { - if m := nicknameRe.FindStringSubmatch(field); len(m) > 1 { + if field == "#" { + break + } else if m := nicknameRe.FindStringSubmatch(field); len(m) > 1 { author.nickname = m[1] } else if m := emailRe.FindStringSubmatch(field); len(m) > 1 { author.emails = append(author.emails, m[1]) @@ -186,53 +157,11 @@ func getAuthors() []author { } } - authors = append(authors, author) + authors.add(author) } return authors } -func readAll(path string) []byte { - fd, err := os.Open(path) - if err != nil { - log.Fatal(err) - } - defer fd.Close() - - bs, err := io.ReadAll(fd) - if err != nil { - log.Fatal(err) - } - - return bs -} - -// Add number of commits per author to the author list. -func getContributions(authors []author) { - buf := new(bytes.Buffer) - cmd := exec.Command("git", "log", "--pretty=format:%ae") - cmd.Stdout = buf - err := cmd.Run() - if err != nil { - log.Fatal(err) - } - -next: - for _, line := range strings.Split(buf.String(), "\n") { - for i := range authors { - for _, email := range authors[i].emails { - if email == line { - authors[i].commits++ - continue next - } - } - } - } - - for i := range authors { - authors[i].log10commits = int(math.Log10(float64(authors[i].commits + 1))) - } -} - // list of commits that we don't include in our author file; because they // are legacy things that don't affect code, are committed with incorrect // address, or for other reasons. @@ -244,72 +173,93 @@ var excludeCommits = stringSetFromStrings([]string{ "9bdcadf6345aba3a939e9e58d85b89dbe9d44bc9", "b933e9666abdfcd22919dd458c930d944e1e1b7f", "b84d960a81c1282a79e2b9477558de4f1af6faae", + "4dfb9d7c83ed172f12ae19408517961f4a49beeb", }) // allAuthors returns the set of authors in the git commit log, except those // in excluded commits. -func allAuthors() map[string]string { - // Format is hash, email, name, newline, body. The body is indented with - // one space, to differentiate from the hash lines. - args := append([]string{"log", "--format=%H %ae %an%n%w(,1,1)%b"}) - cmd := exec.Command("git", args...) - bs, err := cmd.Output() +func addAuthors(authors *authorSet) { + // All existing source-tracked files + bs, err := exec.Command("git", "ls-tree", "-r", "HEAD", "--name-only").CombinedOutput() if err != nil { - log.Fatal("git:", err) + fmt.Println(string(bs)) + log.Fatal("git ls-tree:", err) } + files := strings.Split(string(bs), "\n") + files = slices.DeleteFunc(files, func(s string) bool { + return !(strings.HasPrefix(s, "assets/") || + strings.HasPrefix(s, "cmd/") || + strings.HasPrefix(s, "etc/") || + strings.HasPrefix(s, "gui/") || + strings.HasPrefix(s, "internal/") || + strings.HasPrefix(s, "lib/") || + strings.HasPrefix(s, "proto/") || + strings.HasPrefix(s, "script/") || + strings.HasPrefix(s, "test/") || + strings.HasPrefix(s, "Dockerfile") || + s == "build.go") + }) coAuthoredPrefix := "Co-authored-by: " - names := make(map[string]string) - skipCommit := false - for _, line := range bytes.Split(bs, []byte{'\n'}) { - if len(line) == 0 { - continue + for _, file := range files { + // All commits affecting those files, following any renames to their + // origin. Format is hash, email, name, newline, body. The body is + // indented with one space, to differentiate from the hash lines. + args := []string{"log", "--format=%H %ae %an%n%w(,1,1)%b", "--follow", "--", file} + bs, err = exec.Command("git", args...).CombinedOutput() + if err != nil { + fmt.Println(string(bs)) + log.Fatal("git log:", err) } - switch line[0] { - case ' ': - // Look for Co-authored-by: lines in the commit body. - if skipCommit { + skipCommit := false + var hash, email, name string + for _, line := range bytes.Split(bs, []byte{'\n'}) { + if len(line) == 0 { continue } - line = line[1:] - if bytes.HasPrefix(line, []byte(coAuthoredPrefix)) { - // Co-authored-by: Name Name - line = line[len(coAuthoredPrefix):] - if name, email, ok := strings.Cut(string(line), "<"); ok { - name = strings.TrimSpace(name) - email = strings.Trim(strings.TrimSpace(email), "<>") - if email == "@" { - // GitHub special for users who hide their email. - continue - } - if names[email] == "" { - names[email] = name + switch line[0] { + case ' ': + // Look for Co-authored-by: lines in the commit body. + if skipCommit { + continue + } + + line = line[1:] + if bytes.HasPrefix(line, []byte(coAuthoredPrefix)) { + // Co-authored-by: Name Name + line = line[len(coAuthoredPrefix):] + if name, email, ok := strings.Cut(string(line), "<"); ok { + name = strings.TrimSpace(name) + email = strings.Trim(strings.TrimSpace(email), "<>") + if email == "@" { + // GitHub special for users who hide their email. + continue + } + authors.setName(email, name) + authors.addCommit(email, hash) } } - } - default: // hash email name - fields := strings.SplitN(string(line), " ", 3) - if len(fields) != 3 { - continue - } - hash, email, name := fields[0], fields[1], fields[2] + default: // hash email name + fields := strings.SplitN(string(line), " ", 3) + if len(fields) != 3 { + continue + } + hash, email, name = fields[0], fields[1], fields[2] - if excludeCommits.has(hash) { - skipCommit = true - continue - } - skipCommit = false + if excludeCommits.has(hash) { + skipCommit = true + continue + } + skipCommit = false - if names[email] == "" { - names[email] = name + authors.setName(email, name) + authors.addCommit(email, hash) } } } - - return names } // A simple string set type @@ -333,12 +283,68 @@ func (s stringSet) has(e string) bool { return ok } -func (s stringSet) except(other stringSet) stringSet { - diff := make(stringSet) - for e := range s { - if !other.has(e) { - diff.add(e) +// A set of authors + +type authorSet struct { + authors []author + emails map[string]int // email to author index + commits map[string]stringSet // email to commit hashes +} + +func (a *authorSet) add(author author) { + for _, e := range author.emails { + if idx, ok := a.emails[e]; ok { + emails := append(author.emails, a.authors[idx].emails...) + slices.Sort(emails) + emails = slices.Compact(emails) + a.authors[idx].name = author.name + a.authors[idx].emails = emails + + for _, e := range emails { + a.emails[e] = idx + } + return } } - return diff + + for _, e := range author.emails { + a.emails[e] = len(a.authors) + } + a.authors = append(a.authors, author) +} + +func (a *authorSet) setName(email, name string) { + idx, ok := a.emails[email] + if !ok { + a.emails[email] = len(a.authors) + a.authors = append(a.authors, author{name: name, emails: []string{email}}) + } else if a.authors[idx].name == "" { + a.authors[idx].name = name + } +} + +func (a *authorSet) addCommit(email, hash string) { + ss, ok := a.commits[email] + if !ok { + ss = make(stringSet) + a.commits[email] = ss + } + ss.add(hash) +} + +func (a *authorSet) filteredAuthors() []author { + authors := make([]author, len(a.authors)) + copy(authors, a.authors) + for i, author := range authors { + for _, e := range author.emails { + authors[i].commits += len(a.commits[e]) + } + } + authors = slices.DeleteFunc(authors, func(a author) bool { + return a.commits == 0 || authorBotsRe.MatchString(a.name) + }) + for i := range authors { + authors[i].log10commits = int(math.Log10(float64(authors[i].commits))) + } + return authors } diff --git a/test/h1/config.xml b/test/h1/config.xml index c55cc5349..c7e965a88 100644 --- a/test/h1/config.xml +++ b/test/h1/config.xml @@ -24,7 +24,6 @@ false false false - 25 .stfolder false 0 @@ -153,7 +152,6 @@ false false false - 25 .stfolder false 0 diff --git a/test/h2/config.xml b/test/h2/config.xml index 8a2e1ab9b..81276c1d1 100644 --- a/test/h2/config.xml +++ b/test/h2/config.xml @@ -24,7 +24,6 @@ false false false - 25 .stfolder false 0 @@ -151,7 +150,6 @@ false false false - 25 .stfolder false 0 diff --git a/test/h3/config.xml b/test/h3/config.xml index ac99d8102..440c15b0d 100644 --- a/test/h3/config.xml +++ b/test/h3/config.xml @@ -20,7 +20,6 @@ false false false - 25 .stfolder false 0 @@ -50,7 +49,6 @@ false false false - 25 .stfolder false 0 diff --git a/test/h4/config.xml b/test/h4/config.xml index d0627cd91..8347f39c6 100644 --- a/test/h4/config.xml +++ b/test/h4/config.xml @@ -17,7 +17,6 @@ false false false - 25 .stfolder false 0 diff --git a/tools.go b/tools.go index 0ce34255f..111b11fa3 100644 --- a/tools.go +++ b/tools.go @@ -9,6 +9,7 @@ package tools import ( _ "github.com/calmh/xdr" + _ "github.com/coreos/go-semver/semver" _ "github.com/maxbrunsfeld/counterfeiter/v6" _ "golang.org/x/tools/cmd/goimports" )